Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
63
venv/Lib/site-packages/skimage/io/__init__.py
Normal file
63
venv/Lib/site-packages/skimage/io/__init__.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
"""Utilities to read and write images in various formats.
|
||||
|
||||
The following plug-ins are available:
|
||||
|
||||
"""
|
||||
|
||||
from .manage_plugins import *
|
||||
from .sift import *
|
||||
from .collection import *
|
||||
|
||||
from ._io import *
|
||||
from ._image_stack import *
|
||||
|
||||
|
||||
reset_plugins()
|
||||
|
||||
WRAP_LEN = 73
|
||||
|
||||
|
||||
def _separator(char, lengths):
|
||||
return [char * separator_length for separator_length in lengths]
|
||||
|
||||
|
||||
def _format_plugin_info_table(info_table, column_lengths):
|
||||
"""Add separators and column titles to plugin info table."""
|
||||
info_table.insert(0, _separator('=', column_lengths))
|
||||
info_table.insert(1, ('Plugin', 'Description'))
|
||||
info_table.insert(2, _separator('-', column_lengths))
|
||||
info_table.append(_separator('=', column_lengths))
|
||||
|
||||
|
||||
def _update_doc(doc):
|
||||
"""Add a list of plugins to the module docstring, formatted as
|
||||
a ReStructuredText table.
|
||||
|
||||
"""
|
||||
from textwrap import wrap
|
||||
|
||||
info_table = [(p, plugin_info(p).get('description', 'no description'))
|
||||
for p in available_plugins if not p == 'test']
|
||||
|
||||
if len(info_table) > 0:
|
||||
name_length = max([len(n) for (n, _) in info_table])
|
||||
else:
|
||||
name_length = 0
|
||||
|
||||
description_length = WRAP_LEN - 1 - name_length
|
||||
column_lengths = [name_length, description_length]
|
||||
_format_plugin_info_table(info_table, column_lengths)
|
||||
|
||||
for (name, plugin_description) in info_table:
|
||||
description_lines = wrap(plugin_description, description_length)
|
||||
name_column = [name]
|
||||
name_column.extend(['' for _ in range(len(description_lines) - 1)])
|
||||
for name, description in zip(name_column, description_lines):
|
||||
doc += "%s %s\n" % (name.ljust(name_length), description)
|
||||
doc = doc.strip()
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
if __doc__ is not None:
|
||||
__doc__ = _update_doc(__doc__)
|
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/skimage/io/__pycache__/_io.cpython-36.pyc
Normal file
BIN
venv/Lib/site-packages/skimage/io/__pycache__/_io.cpython-36.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
35
venv/Lib/site-packages/skimage/io/_image_stack.py
Normal file
35
venv/Lib/site-packages/skimage/io/_image_stack.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
__all__ = ['image_stack', 'push', 'pop']
|
||||
|
||||
|
||||
# Shared image queue
|
||||
image_stack = []
|
||||
|
||||
|
||||
def push(img):
|
||||
"""Push an image onto the shared image stack.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img : ndarray
|
||||
Image to push.
|
||||
|
||||
"""
|
||||
if not isinstance(img, np.ndarray):
|
||||
raise ValueError("Can only push ndarrays to the image stack.")
|
||||
|
||||
image_stack.append(img)
|
||||
|
||||
|
||||
def pop():
|
||||
"""Pop an image from the shared image stack.
|
||||
|
||||
Returns
|
||||
-------
|
||||
img : ndarray
|
||||
Image popped from the stack.
|
||||
|
||||
"""
|
||||
return image_stack.pop()
|
201
venv/Lib/site-packages/skimage/io/_io.py
Normal file
201
venv/Lib/site-packages/skimage/io/_io.py
Normal file
|
@ -0,0 +1,201 @@
|
|||
import numpy as np
|
||||
|
||||
from ..io.manage_plugins import call_plugin
|
||||
from ..color.colorconv import rgb2gray, rgba2rgb
|
||||
from .util import file_or_url_context
|
||||
from ..exposure import is_low_contrast
|
||||
from .._shared.utils import warn
|
||||
|
||||
|
||||
__all__ = ['imread', 'imsave', 'imshow', 'show',
|
||||
'imread_collection', 'imshow_collection']
|
||||
|
||||
|
||||
def imread(fname, as_gray=False, plugin=None, **plugin_args):
|
||||
"""Load an image from file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : string
|
||||
Image file name, e.g. ``test.jpg`` or URL.
|
||||
as_gray : bool, optional
|
||||
If True, convert color images to gray-scale (64-bit floats).
|
||||
Images that are already in gray-scale format are not converted.
|
||||
plugin : str, optional
|
||||
Name of plugin to use. By default, the different plugins are
|
||||
tried (starting with imageio) until a suitable
|
||||
candidate is found. If not given and fname is a tiff file, the
|
||||
tifffile plugin will be used.
|
||||
|
||||
Other Parameters
|
||||
----------------
|
||||
plugin_args : keywords
|
||||
Passed to the given plugin.
|
||||
|
||||
Returns
|
||||
-------
|
||||
img_array : ndarray
|
||||
The different color bands/channels are stored in the
|
||||
third dimension, such that a gray-image is MxN, an
|
||||
RGB-image MxNx3 and an RGBA-image MxNx4.
|
||||
|
||||
"""
|
||||
if plugin is None and hasattr(fname, 'lower'):
|
||||
if fname.lower().endswith(('.tiff', '.tif')):
|
||||
plugin = 'tifffile'
|
||||
|
||||
with file_or_url_context(fname) as fname:
|
||||
img = call_plugin('imread', fname, plugin=plugin, **plugin_args)
|
||||
|
||||
if not hasattr(img, 'ndim'):
|
||||
return img
|
||||
|
||||
if img.ndim > 2:
|
||||
if img.shape[-1] not in (3, 4) and img.shape[-3] in (3, 4):
|
||||
img = np.swapaxes(img, -1, -3)
|
||||
img = np.swapaxes(img, -2, -3)
|
||||
|
||||
if as_gray:
|
||||
if img.shape[2] == 4:
|
||||
img = rgba2rgb(img)
|
||||
img = rgb2gray(img)
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def imread_collection(load_pattern, conserve_memory=True,
|
||||
plugin=None, **plugin_args):
|
||||
"""
|
||||
Load a collection of images.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
load_pattern : str or list
|
||||
List of objects to load. These are usually filenames, but may
|
||||
vary depending on the currently active plugin. See the docstring
|
||||
for ``ImageCollection`` for the default behaviour of this parameter.
|
||||
conserve_memory : bool, optional
|
||||
If True, never keep more than one in memory at a specific
|
||||
time. Otherwise, images will be cached once they are loaded.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ic : ImageCollection
|
||||
Collection of images.
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
plugin_args : keywords
|
||||
Passed to the given plugin.
|
||||
|
||||
"""
|
||||
return call_plugin('imread_collection', load_pattern, conserve_memory,
|
||||
plugin=plugin, **plugin_args)
|
||||
|
||||
|
||||
def imsave(fname, arr, plugin=None, check_contrast=True, **plugin_args):
|
||||
"""Save an image to file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Target filename.
|
||||
arr : ndarray of shape (M,N) or (M,N,3) or (M,N,4)
|
||||
Image data.
|
||||
plugin : str, optional
|
||||
Name of plugin to use. By default, the different plugins are
|
||||
tried (starting with imageio) until a suitable
|
||||
candidate is found. If not given and fname is a tiff file, the
|
||||
tifffile plugin will be used.
|
||||
check_contrast : bool, optional
|
||||
Check for low contrast and print warning (default: True).
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
plugin_args : keywords
|
||||
Passed to the given plugin.
|
||||
|
||||
Notes
|
||||
-----
|
||||
When saving a JPEG, the compression ratio may be controlled using the
|
||||
``quality`` keyword argument which is an integer with values in [1, 100]
|
||||
where 1 is worst quality and smallest file size, and 100 is best quality
|
||||
and largest file size (default 75). This is only available when using
|
||||
the PIL and imageio plugins.
|
||||
"""
|
||||
if plugin is None and hasattr(fname, 'lower'):
|
||||
if fname.lower().endswith(('.tiff', '.tif')):
|
||||
plugin = 'tifffile'
|
||||
if arr.dtype == bool:
|
||||
warn('%s is a boolean image: setting True to 255 and False to 0. '
|
||||
'To silence this warning, please convert the image using '
|
||||
'img_as_ubyte.' % fname, stacklevel=2)
|
||||
arr = arr.astype('uint8') * 255
|
||||
if check_contrast and is_low_contrast(arr):
|
||||
warn('%s is a low contrast image' % fname)
|
||||
return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args)
|
||||
|
||||
|
||||
def imshow(arr, plugin=None, **plugin_args):
|
||||
"""Display an image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arr : ndarray or str
|
||||
Image data or name of image file.
|
||||
plugin : str
|
||||
Name of plugin to use. By default, the different plugins are
|
||||
tried (starting with imageio) until a suitable
|
||||
candidate is found.
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
plugin_args : keywords
|
||||
Passed to the given plugin.
|
||||
|
||||
"""
|
||||
if isinstance(arr, str):
|
||||
arr = call_plugin('imread', arr, plugin=plugin)
|
||||
return call_plugin('imshow', arr, plugin=plugin, **plugin_args)
|
||||
|
||||
|
||||
def imshow_collection(ic, plugin=None, **plugin_args):
|
||||
"""Display a collection of images.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ic : ImageCollection
|
||||
Collection to display.
|
||||
plugin : str
|
||||
Name of plugin to use. By default, the different plugins are
|
||||
tried until a suitable candidate is found.
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
plugin_args : keywords
|
||||
Passed to the given plugin.
|
||||
|
||||
"""
|
||||
return call_plugin('imshow_collection', ic, plugin=plugin, **plugin_args)
|
||||
|
||||
|
||||
def show():
|
||||
'''Display pending images.
|
||||
|
||||
Launch the event loop of the current gui plugin, and display all
|
||||
pending images, queued via `imshow`. This is required when using
|
||||
`imshow` from non-interactive scripts.
|
||||
|
||||
A call to `show` will block execution of code until all windows
|
||||
have been closed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import skimage.io as io
|
||||
|
||||
>>> for i in range(4):
|
||||
... ax_im = io.imshow(np.random.rand(50, 50))
|
||||
>>> io.show() # doctest: +SKIP
|
||||
|
||||
'''
|
||||
return call_plugin('_app_show')
|
0
venv/Lib/site-packages/skimage/io/_plugins/__init__.py
Normal file
0
venv/Lib/site-packages/skimage/io/_plugins/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
[fits]
|
||||
description = FITS image reading via PyFITS
|
||||
provides = imread, imread_collection
|
||||
|
146
venv/Lib/site-packages/skimage/io/_plugins/fits_plugin.py
Normal file
146
venv/Lib/site-packages/skimage/io/_plugins/fits_plugin.py
Normal file
|
@ -0,0 +1,146 @@
|
|||
__all__ = ['imread', 'imread_collection']
|
||||
|
||||
import skimage.io as io
|
||||
from warnings import warn
|
||||
|
||||
try:
|
||||
from astropy.io import fits
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Astropy could not be found. It is needed to read FITS files.\n"
|
||||
"Please refer to https://www.astropy.org for installation\n"
|
||||
"instructions.")
|
||||
|
||||
|
||||
def imread(fname):
|
||||
"""Load an image from a FITS file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : string
|
||||
Image file name, e.g. ``test.fits``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
img_array : ndarray
|
||||
Unlike plugins such as PIL, where different color bands/channels are
|
||||
stored in the third dimension, FITS images are greyscale-only and can
|
||||
be N-dimensional, so an array of the native FITS dimensionality is
|
||||
returned, without color channels.
|
||||
|
||||
Currently if no image is found in the file, None will be returned
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Currently FITS ``imread()`` always returns the first image extension when
|
||||
given a Multi-Extension FITS file; use ``imread_collection()`` (which does
|
||||
lazy loading) to get all the extensions at once.
|
||||
|
||||
"""
|
||||
|
||||
hdulist = fits.open(fname)
|
||||
|
||||
# Iterate over FITS image extensions, ignoring any other extension types
|
||||
# such as binary tables, and get the first image data array:
|
||||
img_array = None
|
||||
for hdu in hdulist:
|
||||
if isinstance(hdu, fits.ImageHDU) or \
|
||||
isinstance(hdu, fits.PrimaryHDU):
|
||||
if hdu.data is not None:
|
||||
img_array = hdu.data
|
||||
break
|
||||
hdulist.close()
|
||||
|
||||
return img_array
|
||||
|
||||
|
||||
def imread_collection(load_pattern, conserve_memory=True):
|
||||
"""Load a collection of images from one or more FITS files
|
||||
|
||||
Parameters
|
||||
----------
|
||||
load_pattern : str or list
|
||||
List of extensions to load. Filename globbing is currently
|
||||
unsupported.
|
||||
converve_memory : bool
|
||||
If True, never keep more than one in memory at a specific
|
||||
time. Otherwise, images will be cached once they are loaded.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
ic : ImageCollection
|
||||
Collection of images.
|
||||
|
||||
"""
|
||||
|
||||
intype = type(load_pattern)
|
||||
if intype is not list and intype is not str:
|
||||
raise TypeError("Input must be a filename or list of filenames")
|
||||
|
||||
# Ensure we have a list, otherwise we'll end up iterating over the string:
|
||||
if intype is not list:
|
||||
load_pattern = [load_pattern]
|
||||
|
||||
# Generate a list of filename/extension pairs by opening the list of
|
||||
# files and finding the image extensions in each one:
|
||||
ext_list = []
|
||||
for filename in load_pattern:
|
||||
hdulist = fits.open(filename)
|
||||
for n, hdu in zip(range(len(hdulist)), hdulist):
|
||||
if isinstance(hdu, fits.ImageHDU) or \
|
||||
isinstance(hdu, fits.PrimaryHDU):
|
||||
# Ignore (primary) header units with no data (use '.size'
|
||||
# rather than '.data' to avoid actually loading the image):
|
||||
try:
|
||||
data_size = hdu.size # size is int in Astropy 3.1.2
|
||||
except TypeError:
|
||||
data_size = hdu.size()
|
||||
if data_size > 0:
|
||||
ext_list.append((filename, n))
|
||||
hdulist.close()
|
||||
|
||||
return io.ImageCollection(ext_list, load_func=FITSFactory,
|
||||
conserve_memory=conserve_memory)
|
||||
|
||||
|
||||
def FITSFactory(image_ext):
|
||||
"""Load an image extension from a FITS file and return a NumPy array
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
image_ext : tuple
|
||||
FITS extension to load, in the format ``(filename, ext_num)``.
|
||||
The FITS ``(extname, extver)`` format is unsupported, since this
|
||||
function is not called directly by the user and
|
||||
``imread_collection()`` does the work of figuring out which
|
||||
extensions need loading.
|
||||
|
||||
"""
|
||||
|
||||
# Expect a length-2 tuple with a filename as the first element:
|
||||
if not isinstance(image_ext, tuple):
|
||||
raise TypeError("Expected a tuple")
|
||||
|
||||
if len(image_ext) != 2:
|
||||
raise ValueError("Expected a tuple of length 2")
|
||||
|
||||
filename = image_ext[0]
|
||||
extnum = image_ext[1]
|
||||
|
||||
if type(filename) is not str or type(extnum) is not int:
|
||||
raise ValueError("Expected a (filename, extension) tuple")
|
||||
|
||||
hdulist = fits.open(filename)
|
||||
|
||||
data = hdulist[extnum].data
|
||||
|
||||
hdulist.close()
|
||||
|
||||
if data is None:
|
||||
raise RuntimeError(
|
||||
"Extension %d of %s has no data" % (extnum, filename))
|
||||
|
||||
return data
|
|
@ -0,0 +1,4 @@
|
|||
[gdal]
|
||||
description = Image reading via the GDAL Library (www.gdal.org)
|
||||
provides = imread
|
||||
|
19
venv/Lib/site-packages/skimage/io/_plugins/gdal_plugin.py
Normal file
19
venv/Lib/site-packages/skimage/io/_plugins/gdal_plugin.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
__all__ = ['imread']
|
||||
|
||||
from warnings import warn
|
||||
|
||||
try:
|
||||
import osgeo.gdal as gdal
|
||||
except ImportError:
|
||||
raise ImportError("The GDAL Library could not be found. "
|
||||
"Please refer to http://www.gdal.org/ "
|
||||
"for further instructions.")
|
||||
|
||||
|
||||
def imread(fname):
|
||||
"""Load an image from file.
|
||||
|
||||
"""
|
||||
ds = gdal.Open(fname)
|
||||
|
||||
return ds.ReadAsArray()
|
|
@ -0,0 +1,4 @@
|
|||
[gtk]
|
||||
description = Fast image display using the GTK library
|
||||
provides = imshow, _app_show
|
||||
|
54
venv/Lib/site-packages/skimage/io/_plugins/gtk_plugin.py
Normal file
54
venv/Lib/site-packages/skimage/io/_plugins/gtk_plugin.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
from .util import prepare_for_display, window_manager, GuiLockError
|
||||
|
||||
try:
|
||||
# we try to acquire the gui lock first
|
||||
# or else the gui import might trample another
|
||||
# gui's pyos_inputhook.
|
||||
window_manager.acquire('gtk')
|
||||
except GuiLockError as gle:
|
||||
print(gle)
|
||||
else:
|
||||
try:
|
||||
import gtk
|
||||
except ImportError:
|
||||
print('pygtk libraries not installed.')
|
||||
print('plugin not loaded.')
|
||||
window_manager._release('gtk')
|
||||
else:
|
||||
|
||||
class ImageWindow(gtk.Window):
|
||||
def __init__(self, arr, mgr):
|
||||
gtk.Window.__init__(self)
|
||||
self.mgr = mgr
|
||||
self.mgr.add_window(self)
|
||||
|
||||
self.connect("destroy", self.destroy)
|
||||
|
||||
width = arr.shape[1]
|
||||
height = arr.shape[0]
|
||||
rstride = arr.strides[0]
|
||||
pb = gtk.gdk.pixbuf_new_from_data(arr.data,
|
||||
gtk.gdk.COLORSPACE_RGB,
|
||||
False, 8, width, height,
|
||||
rstride)
|
||||
self.img = gtk.Image()
|
||||
self.img.set_from_pixbuf(pb)
|
||||
|
||||
self.add(self.img)
|
||||
self.img.show()
|
||||
|
||||
def destroy(self, widget, data=None):
|
||||
self.mgr.remove_window(self)
|
||||
|
||||
def imshow(arr):
|
||||
arr = prepare_for_display(arr)
|
||||
|
||||
iw = ImageWindow(arr, window_manager)
|
||||
iw.show()
|
||||
|
||||
def _app_show():
|
||||
if window_manager.has_windows():
|
||||
window_manager.register_callback(gtk.main_quit)
|
||||
gtk.main()
|
||||
else:
|
||||
print('no images to display')
|
|
@ -0,0 +1,3 @@
|
|||
[imageio]
|
||||
description = Image reading via the ImageIO Library
|
||||
provides = imread, imsave
|
10
venv/Lib/site-packages/skimage/io/_plugins/imageio_plugin.py
Normal file
10
venv/Lib/site-packages/skimage/io/_plugins/imageio_plugin.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
__all__ = ['imread', 'imsave']
|
||||
|
||||
from functools import wraps
|
||||
import numpy as np
|
||||
from imageio import imread as imageio_imread, imsave
|
||||
|
||||
|
||||
@wraps(imageio_imread)
|
||||
def imread(*args, **kwargs):
|
||||
return np.asarray(imageio_imread(*args, **kwargs))
|
|
@ -0,0 +1,3 @@
|
|||
[imread]
|
||||
description = Image reading and writing via imread
|
||||
provides = imread, imsave
|
44
venv/Lib/site-packages/skimage/io/_plugins/imread_plugin.py
Normal file
44
venv/Lib/site-packages/skimage/io/_plugins/imread_plugin.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
__all__ = ['imread', 'imsave']
|
||||
|
||||
from ...util.dtype import _convert
|
||||
|
||||
try:
|
||||
import imread as _imread
|
||||
except ImportError:
|
||||
raise ImportError("Imread could not be found"
|
||||
"Please refer to http://pypi.python.org/pypi/imread/ "
|
||||
"for further instructions.")
|
||||
|
||||
|
||||
def imread(fname, dtype=None):
|
||||
"""Load an image from file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Name of input file
|
||||
|
||||
"""
|
||||
im = _imread.imread(fname)
|
||||
if dtype is not None:
|
||||
im = _convert(im, dtype)
|
||||
return im
|
||||
|
||||
|
||||
def imsave(fname, arr, format_str=None):
|
||||
"""Save an image to disk.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Name of destination file.
|
||||
arr : ndarray of uint8 or uint16
|
||||
Array (image) to save.
|
||||
format_str: str,optional
|
||||
Format to save as.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Currently, only 8-bit precision is supported.
|
||||
"""
|
||||
return _imread.imsave(fname, arr, formatstr=format_str)
|
|
@ -0,0 +1,3 @@
|
|||
[matplotlib]
|
||||
description = Display or save images using Matplotlib
|
||||
provides = imshow, imread, imshow_collection, _app_show
|
208
venv/Lib/site-packages/skimage/io/_plugins/matplotlib_plugin.py
Normal file
208
venv/Lib/site-packages/skimage/io/_plugins/matplotlib_plugin.py
Normal file
|
@ -0,0 +1,208 @@
|
|||
from collections import namedtuple
|
||||
import numpy as np
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
import matplotlib.image
|
||||
from ...util import dtype as dtypes
|
||||
from ...exposure import is_low_contrast
|
||||
from ..._shared.utils import warn
|
||||
from math import floor, ceil
|
||||
|
||||
|
||||
_default_colormap = 'gray'
|
||||
_nonstandard_colormap = 'viridis'
|
||||
_diverging_colormap = 'RdBu'
|
||||
|
||||
|
||||
ImageProperties = namedtuple('ImageProperties',
|
||||
['signed', 'out_of_range_float',
|
||||
'low_data_range', 'unsupported_dtype'])
|
||||
|
||||
|
||||
def _get_image_properties(image):
|
||||
"""Determine nonstandard properties of an input image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
The input image.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ip : ImageProperties named tuple
|
||||
The properties of the image:
|
||||
|
||||
- signed: whether the image has negative values.
|
||||
- out_of_range_float: if the image has floating point data
|
||||
outside of [-1, 1].
|
||||
- low_data_range: if the image is in the standard image
|
||||
range (e.g. [0, 1] for a floating point image) but its
|
||||
data range would be too small to display with standard
|
||||
image ranges.
|
||||
- unsupported_dtype: if the image data type is not a
|
||||
standard skimage type, e.g. ``numpy.uint64``.
|
||||
"""
|
||||
immin, immax = np.min(image), np.max(image)
|
||||
imtype = image.dtype.type
|
||||
try:
|
||||
lo, hi = dtypes.dtype_range[imtype]
|
||||
except KeyError:
|
||||
lo, hi = immin, immax
|
||||
|
||||
signed = immin < 0
|
||||
out_of_range_float = (np.issubdtype(image.dtype, np.floating) and
|
||||
(immin < lo or immax > hi))
|
||||
low_data_range = (immin != immax and
|
||||
is_low_contrast(image))
|
||||
unsupported_dtype = image.dtype not in dtypes._supported_types
|
||||
|
||||
return ImageProperties(signed, out_of_range_float,
|
||||
low_data_range, unsupported_dtype)
|
||||
|
||||
|
||||
def _raise_warnings(image_properties):
|
||||
"""Raise the appropriate warning for each nonstandard image type.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image_properties : ImageProperties named tuple
|
||||
The properties of the considered image.
|
||||
"""
|
||||
ip = image_properties
|
||||
if ip.unsupported_dtype:
|
||||
warn("Non-standard image type; displaying image with "
|
||||
"stretched contrast.", stacklevel=3)
|
||||
if ip.low_data_range:
|
||||
warn("Low image data range; displaying image with "
|
||||
"stretched contrast.", stacklevel=3)
|
||||
if ip.out_of_range_float:
|
||||
warn("Float image out of standard range; displaying "
|
||||
"image with stretched contrast.", stacklevel=3)
|
||||
|
||||
|
||||
def _get_display_range(image):
|
||||
"""Return the display range for a given set of image properties.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
The input image.
|
||||
|
||||
Returns
|
||||
-------
|
||||
lo, hi : same type as immin, immax
|
||||
The display range to be used for the input image.
|
||||
cmap : string
|
||||
The name of the colormap to use.
|
||||
"""
|
||||
ip = _get_image_properties(image)
|
||||
immin, immax = np.min(image), np.max(image)
|
||||
if ip.signed:
|
||||
magnitude = max(abs(immin), abs(immax))
|
||||
lo, hi = -magnitude, magnitude
|
||||
cmap = _diverging_colormap
|
||||
elif any(ip):
|
||||
_raise_warnings(ip)
|
||||
lo, hi = immin, immax
|
||||
cmap = _nonstandard_colormap
|
||||
else:
|
||||
lo = 0
|
||||
imtype = image.dtype.type
|
||||
hi = dtypes.dtype_range[imtype][1]
|
||||
cmap = _default_colormap
|
||||
return lo, hi, cmap
|
||||
|
||||
|
||||
def imshow(image, ax=None, show_cbar=None, **kwargs):
|
||||
"""Show the input image and return the current axes.
|
||||
|
||||
By default, the image is displayed in greyscale, rather than
|
||||
the matplotlib default colormap.
|
||||
|
||||
Images are assumed to have standard range for their type. For
|
||||
example, if a floating point image has values in [0, 0.5], the
|
||||
most intense color will be gray50, not white.
|
||||
|
||||
If the image exceeds the standard range, or if the range is too
|
||||
small to display, we fall back on displaying exactly the range of
|
||||
the input image, along with a colorbar to clearly indicate that
|
||||
this range transformation has occurred.
|
||||
|
||||
For signed images, we use a diverging colormap centered at 0.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array, shape (M, N[, 3])
|
||||
The image to display.
|
||||
ax: `matplotlib.axes.Axes`, optional
|
||||
The axis to use for the image, defaults to plt.gca().
|
||||
show_cbar: boolean, optional.
|
||||
Whether to show the colorbar (used to override default behavior).
|
||||
**kwargs : Keyword arguments
|
||||
These are passed directly to `matplotlib.pyplot.imshow`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ax_im : `matplotlib.pyplot.AxesImage`
|
||||
The `AxesImage` object returned by `plt.imshow`.
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
lo, hi, cmap = _get_display_range(image)
|
||||
|
||||
kwargs.setdefault('interpolation', 'nearest')
|
||||
kwargs.setdefault('cmap', cmap)
|
||||
kwargs.setdefault('vmin', lo)
|
||||
kwargs.setdefault('vmax', hi)
|
||||
|
||||
ax = ax or plt.gca()
|
||||
ax_im = ax.imshow(image, **kwargs)
|
||||
if (cmap != _default_colormap and show_cbar is not False) or show_cbar:
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size="5%", pad=0.05)
|
||||
plt.colorbar(ax_im, cax=cax)
|
||||
ax.get_figure().tight_layout()
|
||||
|
||||
return ax_im
|
||||
|
||||
|
||||
def imshow_collection(ic, *args, **kwargs):
|
||||
"""Display all images in the collection.
|
||||
|
||||
Returns
|
||||
-------
|
||||
fig : `matplotlib.figure.Figure`
|
||||
The `Figure` object returned by `plt.subplots`.
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
if len(ic) < 1:
|
||||
raise ValueError('Number of images to plot must be greater than 0')
|
||||
|
||||
# The target is to plot images on a grid with aspect ratio 4:3
|
||||
num_images = len(ic)
|
||||
# Two pairs of `nrows, ncols` are possible
|
||||
k = (num_images * 12)**0.5
|
||||
r1 = max(1, floor(k / 4))
|
||||
r2 = ceil(k / 4)
|
||||
c1 = ceil(num_images / r1)
|
||||
c2 = ceil(num_images / r2)
|
||||
# Select the one which is closer to 4:3
|
||||
if abs(r1 / c1 - 0.75) < abs(r2 / c2 - 0.75):
|
||||
nrows, ncols = r1, c1
|
||||
else:
|
||||
nrows, ncols = r2, c2
|
||||
|
||||
fig, axes = plt.subplots(nrows=nrows, ncols=ncols)
|
||||
ax = np.asarray(axes).ravel()
|
||||
for n, image in enumerate(ic):
|
||||
ax[n].imshow(image, *args, **kwargs)
|
||||
kwargs['ax'] = axes
|
||||
return fig
|
||||
|
||||
|
||||
imread = matplotlib.image.imread
|
||||
|
||||
|
||||
def _app_show():
|
||||
from matplotlib.pyplot import show
|
||||
show()
|
|
@ -0,0 +1,3 @@
|
|||
[pil]
|
||||
description = Image reading via the Python Imaging Library
|
||||
provides = imread, imsave
|
267
venv/Lib/site-packages/skimage/io/_plugins/pil_plugin.py
Normal file
267
venv/Lib/site-packages/skimage/io/_plugins/pil_plugin.py
Normal file
|
@ -0,0 +1,267 @@
|
|||
__all__ = ['imread', 'imsave']
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
import warnings
|
||||
import numpy as np
|
||||
from PIL import Image, __version__ as pil_version
|
||||
|
||||
from ...util import img_as_ubyte, img_as_uint
|
||||
|
||||
|
||||
def imread(fname, dtype=None, img_num=None, **kwargs):
|
||||
"""Load an image from file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str or file
|
||||
File name or file-like-object.
|
||||
dtype : numpy dtype object or string specifier
|
||||
Specifies data type of array elements.
|
||||
img_num : int, optional
|
||||
Specifies which image to read in a file with multiple images
|
||||
(zero-indexed).
|
||||
kwargs : keyword pairs, optional
|
||||
Addition keyword arguments to pass through.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Files are read using the Python Imaging Library.
|
||||
See PIL docs [1]_ for a list of supported formats.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
|
||||
"""
|
||||
if isinstance(fname, str):
|
||||
with open(fname, 'rb') as f:
|
||||
im = Image.open(f)
|
||||
return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
|
||||
else:
|
||||
im = Image.open(fname)
|
||||
if im.format == 'MPO' and LooseVersion(pil_version) < '6.0.0':
|
||||
warnings.warn("You are trying to read a MPO image. "
|
||||
"To ensure a good support of this format, "
|
||||
"please upgrade pillow to 6.0.0 version or later.",
|
||||
stacklevel=2)
|
||||
return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
|
||||
|
||||
|
||||
def pil_to_ndarray(image, dtype=None, img_num=None):
|
||||
"""Import a PIL Image object to an ndarray, in memory.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
Refer to ``imread``.
|
||||
|
||||
"""
|
||||
try:
|
||||
# this will raise an IOError if the file is not readable
|
||||
image.getdata()[0]
|
||||
except IOError as e:
|
||||
site = "http://pillow.readthedocs.org/en/latest/installation.html#external-libraries"
|
||||
pillow_error_message = str(e)
|
||||
error_message = ('Could not load "%s" \n'
|
||||
'Reason: "%s"\n'
|
||||
'Please see documentation at: %s'
|
||||
% (image.filename, pillow_error_message, site))
|
||||
raise ValueError(error_message)
|
||||
frames = []
|
||||
grayscale = None
|
||||
i = 0
|
||||
while 1:
|
||||
try:
|
||||
image.seek(i)
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
frame = image
|
||||
|
||||
if img_num is not None and img_num != i:
|
||||
image.getdata()[0]
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if image.format == 'PNG' and image.mode == 'I' and dtype is None:
|
||||
dtype = 'uint16'
|
||||
|
||||
if image.mode == 'P':
|
||||
if grayscale is None:
|
||||
grayscale = _palette_is_grayscale(image)
|
||||
|
||||
if grayscale:
|
||||
frame = image.convert('L')
|
||||
else:
|
||||
if image.format == 'PNG' and 'transparency' in image.info:
|
||||
frame = image.convert('RGBA')
|
||||
else:
|
||||
frame = image.convert('RGB')
|
||||
|
||||
elif image.mode == '1':
|
||||
frame = image.convert('L')
|
||||
|
||||
elif 'A' in image.mode:
|
||||
frame = image.convert('RGBA')
|
||||
|
||||
elif image.mode == 'CMYK':
|
||||
frame = image.convert('RGB')
|
||||
|
||||
if image.mode.startswith('I;16'):
|
||||
shape = image.size
|
||||
dtype = '>u2' if image.mode.endswith('B') else '<u2'
|
||||
if 'S' in image.mode:
|
||||
dtype = dtype.replace('u', 'i')
|
||||
frame = np.fromstring(frame.tobytes(), dtype)
|
||||
frame.shape = shape[::-1]
|
||||
|
||||
else:
|
||||
frame = np.array(frame, dtype=dtype)
|
||||
|
||||
frames.append(frame)
|
||||
i += 1
|
||||
|
||||
if img_num is not None:
|
||||
break
|
||||
|
||||
if hasattr(image, 'fp') and image.fp:
|
||||
image.fp.close()
|
||||
|
||||
if img_num is None and len(frames) > 1:
|
||||
return np.array(frames)
|
||||
elif frames:
|
||||
return frames[0]
|
||||
elif img_num:
|
||||
raise IndexError('Could not find image #%s' % img_num)
|
||||
|
||||
|
||||
def _palette_is_grayscale(pil_image):
|
||||
"""Return True if PIL image in palette mode is grayscale.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pil_image : PIL image
|
||||
PIL Image that is in Palette mode.
|
||||
|
||||
Returns
|
||||
-------
|
||||
is_grayscale : bool
|
||||
True if all colors in image palette are gray.
|
||||
"""
|
||||
if pil_image.mode != 'P':
|
||||
raise ValueError('pil_image.mode must be equal to "P".')
|
||||
# get palette as an array with R, G, B columns
|
||||
palette = np.asarray(pil_image.getpalette()).reshape((256, 3))
|
||||
# Not all palette colors are used; unused colors have junk values.
|
||||
start, stop = pil_image.getextrema()
|
||||
valid_palette = palette[start:stop + 1]
|
||||
# Image is grayscale if channel differences (R - G and G - B)
|
||||
# are all zero.
|
||||
return np.allclose(np.diff(valid_palette), 0)
|
||||
|
||||
|
||||
def ndarray_to_pil(arr, format_str=None):
|
||||
"""Export an ndarray to a PIL object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
Refer to ``imsave``.
|
||||
|
||||
"""
|
||||
if arr.ndim == 3:
|
||||
arr = img_as_ubyte(arr)
|
||||
mode = {3: 'RGB', 4: 'RGBA'}[arr.shape[2]]
|
||||
|
||||
elif format_str in ['png', 'PNG']:
|
||||
mode = 'I;16'
|
||||
mode_base = 'I'
|
||||
|
||||
if arr.dtype.kind == 'f':
|
||||
arr = img_as_uint(arr)
|
||||
|
||||
elif arr.max() < 256 and arr.min() >= 0:
|
||||
arr = arr.astype(np.uint8)
|
||||
mode = mode_base = 'L'
|
||||
|
||||
else:
|
||||
arr = img_as_uint(arr)
|
||||
|
||||
else:
|
||||
arr = img_as_ubyte(arr)
|
||||
mode = 'L'
|
||||
mode_base = 'L'
|
||||
|
||||
try:
|
||||
array_buffer = arr.tobytes()
|
||||
except AttributeError:
|
||||
array_buffer = arr.tostring() # Numpy < 1.9
|
||||
|
||||
if arr.ndim == 2:
|
||||
im = Image.new(mode_base, arr.T.shape)
|
||||
try:
|
||||
im.frombytes(array_buffer, 'raw', mode)
|
||||
except AttributeError:
|
||||
im.fromstring(array_buffer, 'raw', mode) # PIL 1.1.7
|
||||
else:
|
||||
image_shape = (arr.shape[1], arr.shape[0])
|
||||
try:
|
||||
im = Image.frombytes(mode, image_shape, array_buffer)
|
||||
except AttributeError:
|
||||
im = Image.fromstring(mode, image_shape, array_buffer) # PIL 1.1.7
|
||||
return im
|
||||
|
||||
|
||||
def imsave(fname, arr, format_str=None, **kwargs):
|
||||
"""Save an image to disk.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str or file-like object
|
||||
Name of destination file.
|
||||
arr : ndarray of uint8 or float
|
||||
Array (image) to save. Arrays of data-type uint8 should have
|
||||
values in [0, 255], whereas floating-point arrays must be
|
||||
in [0, 1].
|
||||
format_str: str
|
||||
Format to save as, this is defaulted to PNG if using a file-like
|
||||
object; this will be derived from the extension if fname is a string
|
||||
kwargs: dict
|
||||
Keyword arguments to the Pillow save function (or tifffile save
|
||||
function, for Tiff files). These are format dependent. For example,
|
||||
Pillow's JPEG save function supports an integer ``quality`` argument
|
||||
with values in [1, 95], while TIFFFile supports a ``compress``
|
||||
integer argument with values in [0, 9].
|
||||
|
||||
Notes
|
||||
-----
|
||||
Use the Python Imaging Library.
|
||||
See PIL docs [1]_ for a list of other supported formats.
|
||||
All images besides single channel PNGs are converted using `img_as_uint8`.
|
||||
Single Channel PNGs have the following behavior:
|
||||
- Integer values in [0, 255] and Boolean types -> img_as_uint8
|
||||
- Floating point and other integers -> img_as_uint16
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
|
||||
"""
|
||||
# default to PNG if file-like object
|
||||
if not isinstance(fname, str) and format_str is None:
|
||||
format_str = "PNG"
|
||||
# Check for png in filename
|
||||
if (isinstance(fname, str)
|
||||
and fname.lower().endswith(".png")):
|
||||
format_str = "PNG"
|
||||
|
||||
arr = np.asanyarray(arr)
|
||||
|
||||
if arr.dtype.kind == 'b':
|
||||
arr = arr.astype(np.uint8)
|
||||
|
||||
if arr.ndim not in (2, 3):
|
||||
raise ValueError("Invalid shape for image array: %s" % (arr.shape, ))
|
||||
|
||||
if arr.ndim == 3:
|
||||
if arr.shape[2] not in (3, 4):
|
||||
raise ValueError("Invalid number of channels in image array.")
|
||||
|
||||
img = ndarray_to_pil(arr, format_str=format_str)
|
||||
img.save(fname, format=format_str, **kwargs)
|
342
venv/Lib/site-packages/skimage/io/_plugins/q_color_mixer.py
Normal file
342
venv/Lib/site-packages/skimage/io/_plugins/q_color_mixer.py
Normal file
|
@ -0,0 +1,342 @@
|
|||
# the module for the qt color_mixer plugin
|
||||
from qtpy import QtCore
|
||||
from qtpy.QtWidgets import (QWidget, QStackedWidget, QSlider, QGridLayout,
|
||||
QLabel, QFrame, QComboBox, QRadioButton,
|
||||
QPushButton)
|
||||
|
||||
from .util import ColorMixer
|
||||
|
||||
|
||||
class IntelligentSlider(QWidget):
|
||||
''' A slider that adds a 'name' attribute and calls a callback
|
||||
with 'name' as an argument to the registered callback.
|
||||
|
||||
This allows you to create large groups of sliders in a loop,
|
||||
but still keep track of the individual events
|
||||
|
||||
It also prints a label below the slider.
|
||||
|
||||
The range of the slider is hardcoded from zero - 1000,
|
||||
but it supports a conversion factor so you can scale the results'''
|
||||
|
||||
def __init__(self, name, a, b, callback):
|
||||
QWidget.__init__(self)
|
||||
self.name = name
|
||||
self.callback = callback
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.manually_triggered = False
|
||||
|
||||
self.slider = QSlider()
|
||||
self.slider.setRange(0, 1000)
|
||||
self.slider.setValue(500)
|
||||
self.slider.valueChanged.connect(self.slider_changed)
|
||||
|
||||
self.name_label = QLabel()
|
||||
self.name_label.setText(self.name)
|
||||
self.name_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
self.value_label = QLabel()
|
||||
self.value_label.setText('%2.2f' % (self.slider.value() * self.a
|
||||
+ self.b))
|
||||
self.value_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
self.layout = QGridLayout(self)
|
||||
self.layout.addWidget(self.name_label, 0, 0)
|
||||
self.layout.addWidget(self.slider, 1, 0, QtCore.Qt.AlignHCenter)
|
||||
self.layout.addWidget(self.value_label, 2, 0)
|
||||
|
||||
# bind this to the valueChanged signal of the slider
|
||||
def slider_changed(self, val):
|
||||
val = self.val()
|
||||
self.value_label.setText(str(val)[:4])
|
||||
|
||||
if not self.manually_triggered:
|
||||
self.callback(self.name, val)
|
||||
|
||||
def set_conv_fac(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
def set_value(self, val):
|
||||
self.manually_triggered = True
|
||||
self.slider.setValue(int((val - self.b) / self.a))
|
||||
self.value_label.setText('%2.2f' % val)
|
||||
self.manually_triggered = False
|
||||
|
||||
def val(self):
|
||||
return self.slider.value() * self.a + self.b
|
||||
|
||||
|
||||
class MixerPanel(QFrame):
|
||||
'''A color mixer to hook up to an image.
|
||||
You pass the image you the panel to operate on
|
||||
and it operates on that image in place. You also
|
||||
pass a callback to be called to trigger a refresh.
|
||||
This callback is called every time the mixer modifies
|
||||
your image.'''
|
||||
def __init__(self, img):
|
||||
QFrame.__init__(self)
|
||||
# self.setFrameStyle(QFrame.Box | QFrame.Sunken)
|
||||
|
||||
self.img = img
|
||||
self.mixer = ColorMixer(self.img)
|
||||
self.callback = None
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# ComboBox
|
||||
#---------------------------------------------------------------
|
||||
|
||||
self.combo_box_entries = ['RGB Color', 'HSV Color',
|
||||
'Brightness/Contrast',
|
||||
'Gamma',
|
||||
'Gamma (Sigmoidal)']
|
||||
self.combo_box = QComboBox()
|
||||
for entry in self.combo_box_entries:
|
||||
self.combo_box.addItem(entry)
|
||||
self.combo_box.currentIndexChanged.connect(self.combo_box_changed)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# RGB color sliders
|
||||
#---------------------------------------------------------------
|
||||
|
||||
# radio buttons
|
||||
self.rgb_add = QRadioButton('Additive')
|
||||
self.rgb_mul = QRadioButton('Multiplicative')
|
||||
self.rgb_mul.toggled.connect(self.rgb_radio_changed)
|
||||
self.rgb_add.toggled.connect(self.rgb_radio_changed)
|
||||
|
||||
# sliders
|
||||
rs = IntelligentSlider('R', 0.51, -255, self.rgb_changed)
|
||||
gs = IntelligentSlider('G', 0.51, -255, self.rgb_changed)
|
||||
bs = IntelligentSlider('B', 0.51, -255, self.rgb_changed)
|
||||
self.rs = rs
|
||||
self.gs = gs
|
||||
self.bs = bs
|
||||
|
||||
self.rgb_widget = QWidget()
|
||||
self.rgb_widget.layout = QGridLayout(self.rgb_widget)
|
||||
self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0, 1, 3)
|
||||
self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0, 1, 3)
|
||||
self.rgb_widget.layout.addWidget(self.rs, 2, 0)
|
||||
self.rgb_widget.layout.addWidget(self.gs, 2, 1)
|
||||
self.rgb_widget.layout.addWidget(self.bs, 2, 2)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# HSV sliders
|
||||
#---------------------------------------------------------------
|
||||
# radio buttons
|
||||
self.hsv_add = QRadioButton('Additive')
|
||||
self.hsv_mul = QRadioButton('Multiplicative')
|
||||
self.hsv_mul.toggled.connect(self.hsv_radio_changed)
|
||||
self.hsv_mul.toggled.connect(self.hsv_radio_changed)
|
||||
|
||||
# sliders
|
||||
hs = IntelligentSlider('H', 0.36, -180, self.hsv_changed)
|
||||
ss = IntelligentSlider('S', 0.002, 0, self.hsv_changed)
|
||||
vs = IntelligentSlider('V', 0.002, 0, self.hsv_changed)
|
||||
self.hs = hs
|
||||
self.ss = ss
|
||||
self.vs = vs
|
||||
|
||||
self.hsv_widget = QWidget()
|
||||
self.hsv_widget.layout = QGridLayout(self.hsv_widget)
|
||||
self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0, 1, 3)
|
||||
self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0, 1, 3)
|
||||
self.hsv_widget.layout.addWidget(self.hs, 2, 0)
|
||||
self.hsv_widget.layout.addWidget(self.ss, 2, 1)
|
||||
self.hsv_widget.layout.addWidget(self.vs, 2, 2)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Brightness/Contrast sliders
|
||||
#---------------------------------------------------------------
|
||||
# sliders
|
||||
cont = IntelligentSlider('x', 0.002, 0, self.bright_changed)
|
||||
bright = IntelligentSlider('+', 0.51, -255, self.bright_changed)
|
||||
self.cont = cont
|
||||
self.bright = bright
|
||||
|
||||
# layout
|
||||
self.bright_widget = QWidget()
|
||||
self.bright_widget.layout = QGridLayout(self.bright_widget)
|
||||
self.bright_widget.layout.addWidget(self.cont, 0, 0)
|
||||
self.bright_widget.layout.addWidget(self.bright, 0, 1)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Gamma Slider
|
||||
#----------------------------------------------------------------------
|
||||
gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed)
|
||||
self.gamma = gamma
|
||||
|
||||
# layout
|
||||
self.gamma_widget = QWidget()
|
||||
self.gamma_widget.layout = QGridLayout(self.gamma_widget)
|
||||
self.gamma_widget.layout.addWidget(self.gamma, 0, 0)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Sigmoid Gamma sliders
|
||||
#---------------------------------------------------------------
|
||||
# sliders
|
||||
alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed)
|
||||
beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed)
|
||||
self.a_gamma = alpha
|
||||
self.b_gamma = beta
|
||||
|
||||
# layout
|
||||
self.sig_gamma_widget = QWidget()
|
||||
self.sig_gamma_widget.layout = QGridLayout(self.sig_gamma_widget)
|
||||
self.sig_gamma_widget.layout.addWidget(self.a_gamma, 0, 0)
|
||||
self.sig_gamma_widget.layout.addWidget(self.b_gamma, 0, 1)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Buttons
|
||||
#---------------------------------------------------------------
|
||||
self.commit_button = QPushButton('Commit')
|
||||
self.commit_button.clicked.connect(self.commit_changes)
|
||||
self.revert_button = QPushButton('Revert')
|
||||
self.revert_button.clicked.connect(self.revert_changes)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Mixer Layout
|
||||
#---------------------------------------------------------------
|
||||
self.sliders = QStackedWidget()
|
||||
self.sliders.addWidget(self.rgb_widget)
|
||||
self.sliders.addWidget(self.hsv_widget)
|
||||
self.sliders.addWidget(self.bright_widget)
|
||||
self.sliders.addWidget(self.gamma_widget)
|
||||
self.sliders.addWidget(self.sig_gamma_widget)
|
||||
|
||||
self.layout = QGridLayout(self)
|
||||
self.layout.addWidget(self.combo_box, 0, 0)
|
||||
self.layout.addWidget(self.sliders, 1, 0)
|
||||
self.layout.addWidget(self.commit_button, 2, 0)
|
||||
self.layout.addWidget(self.revert_button, 3, 0)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# State Initialization
|
||||
#---------------------------------------------------------------
|
||||
|
||||
self.combo_box.setCurrentIndex(0)
|
||||
self.rgb_mul.setChecked(True)
|
||||
self.hsv_mul.setChecked(True)
|
||||
|
||||
def set_callback(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
def combo_box_changed(self, index):
|
||||
self.sliders.setCurrentIndex(index)
|
||||
self.reset()
|
||||
|
||||
def rgb_radio_changed(self):
|
||||
self.reset()
|
||||
|
||||
def hsv_radio_changed(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.reset_sliders()
|
||||
self.mixer.set_to_stateimg()
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
def reset_sliders(self):
|
||||
# handle changing the conversion factors necessary
|
||||
if self.rgb_add.isChecked():
|
||||
self.rs.set_conv_fac(0.51, -255)
|
||||
self.rs.set_value(0)
|
||||
self.gs.set_conv_fac(0.51, -255)
|
||||
self.gs.set_value(0)
|
||||
self.bs.set_conv_fac(0.51, -255)
|
||||
self.bs.set_value(0)
|
||||
else:
|
||||
self.rs.set_conv_fac(0.002, 0)
|
||||
self.rs.set_value(1.)
|
||||
self.gs.set_conv_fac(0.002, 0)
|
||||
self.gs.set_value(1.)
|
||||
self.bs.set_conv_fac(0.002, 0)
|
||||
self.bs.set_value(1.)
|
||||
|
||||
self.hs.set_value(0)
|
||||
if self.hsv_add.isChecked():
|
||||
self.ss.set_conv_fac(0.002, -1)
|
||||
self.ss.set_value(0)
|
||||
self.vs.set_conv_fac(0.002, -1)
|
||||
self.vs.set_value(0)
|
||||
else:
|
||||
self.ss.set_conv_fac(0.002, 0)
|
||||
self.ss.set_value(1.)
|
||||
self.vs.set_conv_fac(0.002, 0)
|
||||
self.vs.set_value(1.)
|
||||
|
||||
self.bright.set_value(0)
|
||||
self.cont.set_value(1.)
|
||||
|
||||
self.gamma.set_value(1)
|
||||
self.a_gamma.set_value(1)
|
||||
self.b_gamma.set_value(0.5)
|
||||
|
||||
def rgb_changed(self, name, val):
|
||||
if name == 'R':
|
||||
channel = self.mixer.RED
|
||||
elif name == 'G':
|
||||
channel = self.mixer.GREEN
|
||||
else:
|
||||
channel = self.mixer.BLUE
|
||||
|
||||
if self.rgb_mul.isChecked():
|
||||
self.mixer.multiply(channel, val)
|
||||
elif self.rgb_add.isChecked():
|
||||
self.mixer.add(channel, val)
|
||||
else:
|
||||
pass
|
||||
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
def hsv_changed(self, name, val):
|
||||
h = self.hs.val()
|
||||
s = self.ss.val()
|
||||
v = self.vs.val()
|
||||
|
||||
if self.hsv_mul.isChecked():
|
||||
self.mixer.hsv_multiply(h, s, v)
|
||||
elif self.hsv_add.isChecked():
|
||||
self.mixer.hsv_add(h, s, v)
|
||||
else:
|
||||
pass
|
||||
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
def bright_changed(self, name, val):
|
||||
b = self.bright.val()
|
||||
c = self.cont.val()
|
||||
self.mixer.brightness(c, b)
|
||||
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
def gamma_changed(self, name, val):
|
||||
self.mixer.gamma(val)
|
||||
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
def sig_gamma_changed(self, name, val):
|
||||
ag = self.a_gamma.val()
|
||||
bg = self.b_gamma.val()
|
||||
self.mixer.sigmoid_gamma(ag, bg)
|
||||
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
def commit_changes(self):
|
||||
self.mixer.commit_changes()
|
||||
self.reset_sliders()
|
||||
|
||||
def revert_changes(self):
|
||||
self.mixer.revert()
|
||||
self.reset_sliders()
|
||||
|
||||
if self.callback:
|
||||
self.callback()
|
143
venv/Lib/site-packages/skimage/io/_plugins/q_histogram.py
Normal file
143
venv/Lib/site-packages/skimage/io/_plugins/q_histogram.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
import numpy as np
|
||||
|
||||
from qtpy.QtGui import QPainter, QColor
|
||||
from qtpy.QtWidgets import QWidget, QGridLayout, QFrame
|
||||
|
||||
from .util import histograms
|
||||
|
||||
|
||||
class ColorHistogram(QWidget):
|
||||
'''A Class which draws a scaling histogram in
|
||||
a widget.
|
||||
|
||||
Where counts are the bin values in the histogram
|
||||
and colormap is a tuple of (R, G, B) tuples the same length
|
||||
as counts. These are the colors to apply to the histogram bars.
|
||||
Colormap can also contain a single tuple (R, G, B), in which case this is
|
||||
the color applied to all bars of that histogram.
|
||||
|
||||
The histogram assumes the bins were evenly spaced.
|
||||
'''
|
||||
|
||||
def __init__(self, counts, colormap):
|
||||
QWidget.__init__(self)
|
||||
self._validate_input(counts, colormap)
|
||||
self.counts = counts
|
||||
self.n = np.sum(self.counts)
|
||||
self.colormap = colormap
|
||||
self.setMinimumSize(100, 50)
|
||||
|
||||
def _validate_input(self, counts, colormap):
|
||||
if len(counts) != len(colormap):
|
||||
if len(colormap) != 3:
|
||||
msg = '''Colormap must be a list of 3-tuples the same
|
||||
length as counts or a 3-tuple'''
|
||||
raise ValueError(msg)
|
||||
|
||||
def paintEvent(self, evt):
|
||||
# get the widget dimensions
|
||||
orig_width = self.width()
|
||||
orig_height = self.height()
|
||||
|
||||
# fill perc % of the widget
|
||||
perc = 1
|
||||
width = int(orig_width * perc)
|
||||
height = int(orig_height * perc)
|
||||
|
||||
# get the starting origin
|
||||
x_orig = int((orig_width - width) / 2)
|
||||
# we want to start at the bottom and draw up.
|
||||
y_orig = orig_height - int((orig_height - height) / 2)
|
||||
|
||||
# a running x-position
|
||||
running_pos = x_orig
|
||||
|
||||
# calculate to number of bars
|
||||
nbars = len(self.counts)
|
||||
|
||||
# calculate the bar widths, this compilcation is
|
||||
# necessary because integer trunction severely cripples
|
||||
# the layout.
|
||||
remainder = width % nbars
|
||||
bar_width = [int(width / nbars)] * nbars
|
||||
for i in range(remainder):
|
||||
bar_width[i] += 1
|
||||
|
||||
paint = QPainter()
|
||||
paint.begin(self)
|
||||
|
||||
# determine the scaling factor
|
||||
max_val = np.max(self.counts)
|
||||
scale = 1. * height / max_val
|
||||
|
||||
# determine if we have a colormap and drop into the appropriate
|
||||
# loop.
|
||||
if hasattr(self.colormap[0], '__iter__'):
|
||||
# assume we have a colormap
|
||||
for i in range(len(self.counts)):
|
||||
bar_height = self.counts[i]
|
||||
r, g, b = self.colormap[i]
|
||||
paint.setPen(QColor(r, g, b))
|
||||
paint.setBrush(QColor(r, g, b))
|
||||
paint.drawRect(running_pos, y_orig, bar_width[i],
|
||||
-bar_height)
|
||||
running_pos += bar_width[i]
|
||||
|
||||
else:
|
||||
# we have a tuple
|
||||
r, g, b = self.colormap
|
||||
paint.setPen(QColor(r, g, b))
|
||||
paint.setBrush(QColor(r, g, b))
|
||||
for i in range(len(self.counts)):
|
||||
bar_height = self.counts[i] * scale
|
||||
paint.drawRect(running_pos, y_orig, bar_width[i],
|
||||
-bar_height)
|
||||
running_pos += bar_width[i]
|
||||
|
||||
paint.end()
|
||||
|
||||
def update_hist(self, counts, cmap):
|
||||
self._validate_input(counts, cmap)
|
||||
self.counts = counts
|
||||
self.colormap = cmap
|
||||
self.repaint()
|
||||
|
||||
|
||||
class QuadHistogram(QFrame):
|
||||
'''A class which uses ColorHistogram to draw
|
||||
the 4 histograms of an image. R, G, B, and Value.
|
||||
|
||||
The 4 histograms are layout out in a grid,
|
||||
and can be specified horizontal or vertical,
|
||||
and in which order ie. ['R', 'G', 'B', 'V']
|
||||
'''
|
||||
|
||||
def __init__(self, img, layout='vertical', order=['R', 'G', 'B', 'V']):
|
||||
QFrame.__init__(self)
|
||||
|
||||
r, g, b, v = histograms(img, 100)
|
||||
self.r_hist = ColorHistogram(r, (255, 0, 0))
|
||||
self.g_hist = ColorHistogram(g, (0, 255, 0))
|
||||
self.b_hist = ColorHistogram(b, (0, 0, 255))
|
||||
self.v_hist = ColorHistogram(v, (0, 0, 0))
|
||||
|
||||
self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
|
||||
self.layout = QGridLayout(self)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
order_map = {'R': self.r_hist, 'G': self.g_hist, 'B': self.b_hist,
|
||||
'V': self.v_hist}
|
||||
|
||||
if layout == 'vertical':
|
||||
for i in range(len(order)):
|
||||
self.layout.addWidget(order_map[order[i]], i, 0)
|
||||
elif layout == 'horizontal':
|
||||
for i in range(len(order)):
|
||||
self.layout.addWidget(order_map[order[i]], 0, i)
|
||||
|
||||
def update_hists(self, img):
|
||||
r, g, b, v = histograms(img, 100)
|
||||
self.r_hist.update_hist(r, (255, 0, 0))
|
||||
self.g_hist.update_hist(g, (0, 255, 0))
|
||||
self.b_hist.update_hist(b, (0, 0, 255))
|
||||
self.v_hist.update_hist(v, (0, 0, 0))
|
4
venv/Lib/site-packages/skimage/io/_plugins/qt_plugin.ini
Normal file
4
venv/Lib/site-packages/skimage/io/_plugins/qt_plugin.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[qt]
|
||||
description = Fast image display using the Qt library
|
||||
provides = imshow, _app_show, imsave, imread
|
||||
|
151
venv/Lib/site-packages/skimage/io/_plugins/qt_plugin.py
Normal file
151
venv/Lib/site-packages/skimage/io/_plugins/qt_plugin.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
import numpy as np
|
||||
from .util import prepare_for_display, window_manager
|
||||
from ..._shared.utils import warn
|
||||
|
||||
from qtpy.QtWidgets import (QApplication, QLabel, QMainWindow, QWidget,
|
||||
QGridLayout)
|
||||
from qtpy.QtGui import QImage, QPixmap
|
||||
from qtpy import QtCore
|
||||
|
||||
# We try to acquire the gui lock first or else the gui import might
|
||||
# trample another GUI's PyOS_InputHook.
|
||||
window_manager.acquire('qt')
|
||||
|
||||
app = None
|
||||
|
||||
|
||||
class ImageLabel(QLabel):
|
||||
def __init__(self, parent, arr):
|
||||
QLabel.__init__(self)
|
||||
|
||||
# we need to hold a reference to
|
||||
# arr because QImage doesn't copy the data
|
||||
# and the buffer must be alive as long
|
||||
# as the image is alive.
|
||||
self.arr = arr
|
||||
|
||||
# we also need to pass in the row-stride to
|
||||
# the constructor, because we can't guarantee
|
||||
# that every row of the numpy data is
|
||||
# 4-byte aligned. Which Qt would require
|
||||
# if we didn't pass the stride.
|
||||
self.img = QImage(arr.data, arr.shape[1], arr.shape[0],
|
||||
arr.strides[0], QImage.Format_RGB888)
|
||||
self.pm = QPixmap.fromImage(self.img)
|
||||
self.setPixmap(self.pm)
|
||||
self.setAlignment(QtCore.Qt.AlignTop)
|
||||
self.setMinimumSize(100, 100)
|
||||
|
||||
def resizeEvent(self, evt):
|
||||
width = self.width()
|
||||
pm = QPixmap.fromImage(self.img)
|
||||
self.pm = pm.scaledToWidth(width)
|
||||
self.setPixmap(self.pm)
|
||||
|
||||
|
||||
class ImageWindow(QMainWindow):
|
||||
def __init__(self, arr, mgr):
|
||||
QMainWindow.__init__(self)
|
||||
self.setWindowTitle('skimage')
|
||||
self.mgr = mgr
|
||||
self.main_widget = QWidget()
|
||||
self.layout = QGridLayout(self.main_widget)
|
||||
self.setCentralWidget(self.main_widget)
|
||||
|
||||
self.label = ImageLabel(self, arr)
|
||||
self.layout.addWidget(self.label, 0, 0)
|
||||
self.layout.addLayout
|
||||
self.mgr.add_window(self)
|
||||
self.main_widget.show()
|
||||
|
||||
def closeEvent(self, event):
|
||||
# Allow window to be destroyed by removing any
|
||||
# references to it
|
||||
self.mgr.remove_window(self)
|
||||
|
||||
|
||||
def imread(filename):
|
||||
"""
|
||||
Read an image using QT's QImage.load
|
||||
"""
|
||||
qtimg = QImage()
|
||||
if not qtimg.load(filename):
|
||||
# QImage.load() returns false on failure, so raise an exception
|
||||
raise IOError('Unable to load file %s' % filename)
|
||||
if qtimg.depth() == 1:
|
||||
raise IOError('1-bit images currently not supported')
|
||||
# TODO: Warn about other odd formats we don't currently handle properly,
|
||||
# such as the odd 16-bit packed formats QT supports
|
||||
arrayptr = qtimg.bits()
|
||||
# QT may pad the image, so we need to use bytesPerLine, not width for
|
||||
# the conversion to a numpy array
|
||||
bytes_per_pixel = qtimg.depth() // 8
|
||||
pixels_per_line = qtimg.bytesPerLine() // bytes_per_pixel
|
||||
img_size = pixels_per_line * qtimg.height() * bytes_per_pixel
|
||||
arrayptr.setsize(img_size)
|
||||
img = np.array(arrayptr)
|
||||
# Reshape and trim down to correct dimensions
|
||||
if bytes_per_pixel > 1:
|
||||
img = img.reshape((qtimg.height(), pixels_per_line, bytes_per_pixel))
|
||||
img = img[:, :qtimg.width(), :]
|
||||
else:
|
||||
img = img.reshape((qtimg.height(), pixels_per_line))
|
||||
img = img[:, :qtimg.width()]
|
||||
# Strip qt's false alpha channel if needed
|
||||
# and reorder color axes as required
|
||||
if bytes_per_pixel == 4 and not qtimg.hasAlphaChannel():
|
||||
img = img[:, :, 2::-1]
|
||||
elif bytes_per_pixel == 4:
|
||||
img[:, :, 0:3] = img[:, :, 2::-1]
|
||||
return img
|
||||
|
||||
def imshow(arr, fancy=False):
|
||||
global app
|
||||
if not app:
|
||||
app = QApplication([])
|
||||
|
||||
arr = prepare_for_display(arr)
|
||||
|
||||
if not fancy:
|
||||
iw = ImageWindow(arr, window_manager)
|
||||
else:
|
||||
from .skivi import SkiviImageWindow
|
||||
iw = SkiviImageWindow(arr, window_manager)
|
||||
|
||||
iw.show()
|
||||
|
||||
|
||||
def _app_show():
|
||||
global app
|
||||
if app and window_manager.has_windows():
|
||||
app.exec_()
|
||||
else:
|
||||
print('No images to show. See `imshow`.')
|
||||
|
||||
|
||||
def imsave(filename, img, format_str=None):
|
||||
# we can add support for other than 3D uint8 here...
|
||||
img = prepare_for_display(img)
|
||||
qimg = QImage(img.data, img.shape[1], img.shape[0],
|
||||
img.strides[0], QImage.Format_RGB888)
|
||||
if _is_filelike(filename):
|
||||
byte_array = QtCore.QByteArray()
|
||||
qbuffer = QtCore.QBuffer(byte_array)
|
||||
qbuffer.open(QtCore.QIODevice.ReadWrite)
|
||||
saved = qimg.save(qbuffer, format_str.upper())
|
||||
qbuffer.seek(0)
|
||||
filename.write(qbuffer.readAll().data())
|
||||
qbuffer.close()
|
||||
else:
|
||||
saved = qimg.save(filename)
|
||||
if not saved:
|
||||
from textwrap import dedent
|
||||
msg = dedent(
|
||||
'''The image was not saved. Allowable file formats
|
||||
for the QT imsave plugin are:
|
||||
BMP, JPG, JPEG, PNG, PPM, TIFF, XBM, XPM''')
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
def _is_filelike(possible_filelike):
|
||||
return callable(getattr(possible_filelike, 'write', None))
|
|
@ -0,0 +1,3 @@
|
|||
[simpleitk]
|
||||
description = Image reading and writing via SimpleITK
|
||||
provides = imread, imsave
|
|
@ -0,0 +1,21 @@
|
|||
__all__ = ['imread', 'imsave']
|
||||
|
||||
try:
|
||||
import SimpleITK as sitk
|
||||
except ImportError:
|
||||
raise ImportError("SimpleITK could not be found. "
|
||||
"Please try "
|
||||
" easy_install SimpleITK "
|
||||
"or refer to "
|
||||
" http://simpleitk.org/ "
|
||||
"for further instructions.")
|
||||
|
||||
|
||||
def imread(fname):
|
||||
sitk_img = sitk.ReadImage(fname)
|
||||
return sitk.GetArrayFromImage(sitk_img)
|
||||
|
||||
|
||||
def imsave(fname, arr):
|
||||
sitk_img = sitk.GetImageFromArray(arr, isVector=True)
|
||||
sitk.WriteImage(sitk_img, fname)
|
234
venv/Lib/site-packages/skimage/io/_plugins/skivi.py
Normal file
234
venv/Lib/site-packages/skimage/io/_plugins/skivi.py
Normal file
|
@ -0,0 +1,234 @@
|
|||
'''
|
||||
Skivi is written/maintained/developed by:
|
||||
|
||||
S. Chris Colbert - sccolbert@gmail.com
|
||||
|
||||
Skivi is free software and is part of the scikit-image project.
|
||||
|
||||
Skivi is governed by the licenses of the scikit-image project.
|
||||
|
||||
Please report any bugs to the author.
|
||||
|
||||
The skivi module is not meant to be used directly.
|
||||
|
||||
Use skimage.io.imshow(img, fancy=True)'''
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
from qtpy import QtCore, QtWidgets
|
||||
from qtpy.QtWidgets import QMainWindow, QLabel, QWidget, QFrame, QGridLayout
|
||||
from qtpy.QtGui import QImage, QPixmap
|
||||
|
||||
from .q_color_mixer import MixerPanel
|
||||
from .q_histogram import QuadHistogram
|
||||
|
||||
|
||||
class ImageLabel(QLabel):
|
||||
def __init__(self, parent, arr):
|
||||
QLabel.__init__(self)
|
||||
self.parent = parent
|
||||
|
||||
# we need to hold a reference to
|
||||
# arr because QImage doesn't copy the data
|
||||
# and the buffer must be alive as long
|
||||
# as the image is alive.
|
||||
self.arr = arr
|
||||
|
||||
# we also need to pass in the row-stride to
|
||||
# the constructor, because we can't guarantee
|
||||
# that every row of the numpy data is
|
||||
# 4-byte aligned. Which Qt would require
|
||||
# if we didn't pass the stride.
|
||||
self.img = QImage(arr.data, arr.shape[1], arr.shape[0],
|
||||
arr.strides[0], QImage.Format_RGB888)
|
||||
self.pm = QPixmap.fromImage(self.img)
|
||||
self.setPixmap(self.pm)
|
||||
self.setAlignment(QtCore.Qt.AlignTop)
|
||||
self.setMinimumSize(100, 100)
|
||||
self.setMouseTracking(True)
|
||||
|
||||
def mouseMoveEvent(self, evt):
|
||||
self.parent.label_mouseMoveEvent(evt)
|
||||
|
||||
def resizeEvent(self, evt):
|
||||
width = self.width()
|
||||
pm = QPixmap.fromImage(self.img)
|
||||
self.pm = pm.scaledToWidth(width)
|
||||
self.setPixmap(self.pm)
|
||||
|
||||
def update_image(self):
|
||||
width = self.width()
|
||||
pm = QPixmap.fromImage(self.img)
|
||||
pm = pm.scaledToWidth(width)
|
||||
self.setPixmap(pm)
|
||||
|
||||
|
||||
class RGBHSVDisplay(QFrame):
|
||||
def __init__(self):
|
||||
QFrame.__init__(self)
|
||||
self.setFrameStyle(QFrame.Box | QFrame.Sunken)
|
||||
|
||||
self.posx_label = QLabel('X-pos:')
|
||||
self.posx_value = QLabel()
|
||||
self.posy_label = QLabel('Y-pos:')
|
||||
self.posy_value = QLabel()
|
||||
self.r_label = QLabel('R:')
|
||||
self.r_value = QLabel()
|
||||
self.g_label = QLabel('G:')
|
||||
self.g_value = QLabel()
|
||||
self.b_label = QLabel('B:')
|
||||
self.b_value = QLabel()
|
||||
self.h_label = QLabel('H:')
|
||||
self.h_value = QLabel()
|
||||
self.s_label = QLabel('S:')
|
||||
self.s_value = QLabel()
|
||||
self.v_label = QLabel('V:')
|
||||
self.v_value = QLabel()
|
||||
|
||||
self.layout = QGridLayout(self)
|
||||
self.layout.addWidget(self.posx_label, 0, 0)
|
||||
self.layout.addWidget(self.posx_value, 0, 1)
|
||||
self.layout.addWidget(self.posy_label, 1, 0)
|
||||
self.layout.addWidget(self.posy_value, 1, 1)
|
||||
self.layout.addWidget(self.r_label, 0, 2)
|
||||
self.layout.addWidget(self.r_value, 0, 3)
|
||||
self.layout.addWidget(self.g_label, 1, 2)
|
||||
self.layout.addWidget(self.g_value, 1, 3)
|
||||
self.layout.addWidget(self.b_label, 2, 2)
|
||||
self.layout.addWidget(self.b_value, 2, 3)
|
||||
self.layout.addWidget(self.h_label, 0, 4)
|
||||
self.layout.addWidget(self.h_value, 0, 5)
|
||||
self.layout.addWidget(self.s_label, 1, 4)
|
||||
self.layout.addWidget(self.s_value, 1, 5)
|
||||
self.layout.addWidget(self.v_label, 2, 4)
|
||||
self.layout.addWidget(self.v_value, 2, 5)
|
||||
|
||||
def update_vals(self, data):
|
||||
xpos, ypos, r, g, b, h, s, v = data
|
||||
self.posx_value.setText(str(xpos)[:5])
|
||||
self.posy_value.setText(str(ypos)[:5])
|
||||
self.r_value.setText(str(r)[:5])
|
||||
self.g_value.setText(str(g)[:5])
|
||||
self.b_value.setText(str(b)[:5])
|
||||
self.h_value.setText(str(h)[:5])
|
||||
self.s_value.setText(str(s)[:5])
|
||||
self.v_value.setText(str(v)[:5])
|
||||
|
||||
|
||||
class SkiviImageWindow(QMainWindow):
|
||||
def __init__(self, arr, mgr):
|
||||
QMainWindow.__init__(self)
|
||||
|
||||
self.arr = arr
|
||||
|
||||
self.mgr = mgr
|
||||
self.main_widget = QWidget()
|
||||
self.layout = QGridLayout(self.main_widget)
|
||||
self.setCentralWidget(self.main_widget)
|
||||
|
||||
self.label = ImageLabel(self, arr)
|
||||
self.label_container = QFrame()
|
||||
self.label_container.setFrameShape(QFrame.StyledPanel | QFrame.Sunken)
|
||||
self.label_container.setLineWidth(1)
|
||||
|
||||
self.label_container.layout = QGridLayout(self.label_container)
|
||||
self.label_container.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label_container.layout.addWidget(self.label, 0, 0)
|
||||
self.layout.addWidget(self.label_container, 0, 0)
|
||||
|
||||
self.mgr.add_window(self)
|
||||
self.main_widget.show()
|
||||
|
||||
self.setWindowTitle('Skivi - The skimage viewer.')
|
||||
|
||||
self.mixer_panel = MixerPanel(self.arr)
|
||||
self.layout.addWidget(self.mixer_panel, 0, 2)
|
||||
self.mixer_panel.show()
|
||||
self.mixer_panel.set_callback(self.refresh_image)
|
||||
|
||||
self.rgbv_hist = QuadHistogram(self.arr)
|
||||
self.layout.addWidget(self.rgbv_hist, 0, 1)
|
||||
self.rgbv_hist.show()
|
||||
|
||||
self.rgb_hsv_disp = RGBHSVDisplay()
|
||||
self.layout.addWidget(self.rgb_hsv_disp, 1, 0)
|
||||
self.rgb_hsv_disp.show()
|
||||
|
||||
self.layout.setColumnStretch(0, 1)
|
||||
self.layout.setRowStretch(0, 1)
|
||||
|
||||
self.save_file = QtWidgets.QPushButton('Save to File')
|
||||
self.save_file.clicked.connect(self.save_to_file)
|
||||
self.save_stack = QtWidgets.QPushButton('Save to Stack')
|
||||
self.save_stack.clicked.connect(self.save_to_stack)
|
||||
self.save_file.show()
|
||||
self.save_stack.show()
|
||||
|
||||
self.layout.addWidget(self.save_stack, 1, 1)
|
||||
self.layout.addWidget(self.save_file, 1, 2)
|
||||
|
||||
def closeEvent(self, event):
|
||||
# Allow window to be destroyed by removing any
|
||||
# references to it
|
||||
self.mgr.remove_window(self)
|
||||
|
||||
def update_histograms(self):
|
||||
self.rgbv_hist.update_hists(self.arr)
|
||||
|
||||
def refresh_image(self):
|
||||
self.label.update_image()
|
||||
self.update_histograms()
|
||||
|
||||
def scale_mouse_pos(self, x, y):
|
||||
width = self.label.pm.width()
|
||||
height = self.label.pm.height()
|
||||
x_frac = 1. * x / width
|
||||
y_frac = 1. * y / height
|
||||
width = self.arr.shape[1]
|
||||
height = self.arr.shape[0]
|
||||
new_x = int(width * x_frac)
|
||||
new_y = int(height * y_frac)
|
||||
return(new_x, new_y)
|
||||
|
||||
def label_mouseMoveEvent(self, evt):
|
||||
x = evt.x()
|
||||
y = evt.y()
|
||||
x, y = self.scale_mouse_pos(x, y)
|
||||
|
||||
# handle tracking out of array bounds
|
||||
maxw = self.arr.shape[1]
|
||||
maxh = self.arr.shape[0]
|
||||
if x >= maxw or y >= maxh or x < 0 or y < 0:
|
||||
r = g = b = h = s = v = ''
|
||||
else:
|
||||
r = self.arr[y, x, 0]
|
||||
g = self.arr[y, x, 1]
|
||||
b = self.arr[y, x, 2]
|
||||
h, s, v = self.mixer_panel.mixer.rgb_2_hsv_pixel(r, g, b)
|
||||
|
||||
self.rgb_hsv_disp.update_vals((x, y, r, g, b, h, s, v))
|
||||
|
||||
def save_to_stack(self):
|
||||
from ... import io
|
||||
img = self.arr.copy()
|
||||
io.push(img)
|
||||
msg = dedent('''
|
||||
The image has been pushed to the io stack.
|
||||
Use io.pop() to retrieve the most recently
|
||||
pushed image.''')
|
||||
msglabel = QLabel(msg)
|
||||
dialog = QtWidgets.QDialog()
|
||||
ok = QtWidgets.QPushButton('OK', dialog)
|
||||
ok.clicked.connect(dialog.accept)
|
||||
ok.setDefault(True)
|
||||
dialog.layout = QGridLayout(dialog)
|
||||
dialog.layout.addWidget(msglabel, 0, 0, 1, 3)
|
||||
dialog.layout.addWidget(ok, 1, 1)
|
||||
dialog.exec_()
|
||||
|
||||
def save_to_file(self):
|
||||
from ... import io
|
||||
filename = str(QtWidgets.QFileDialog.getSaveFileName())
|
||||
if len(filename) == 0:
|
||||
return
|
||||
io.imsave(filename, self.arr)
|
|
@ -0,0 +1,3 @@
|
|||
[tifffile]
|
||||
description = Load and save TIFF and TIFF-based images using tifffile.py
|
||||
provides = imread, imsave
|
|
@ -0,0 +1,37 @@
|
|||
from warnings import warn
|
||||
|
||||
from tifffile import TiffFile, imsave, parse_kwargs
|
||||
|
||||
|
||||
def imread(fname, **kwargs):
|
||||
"""Load a tiff image from file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str or file
|
||||
File name or file-like-object.
|
||||
kwargs : keyword pairs, optional
|
||||
Additional keyword arguments to pass through (see ``tifffile``'s
|
||||
``imread`` function).
|
||||
|
||||
Notes
|
||||
-----
|
||||
Provided by Christophe Golhke's tifffile.py [1]_, and supports many
|
||||
advanced image types including multi-page and floating point.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py
|
||||
|
||||
"""
|
||||
if 'img_num' in kwargs:
|
||||
kwargs['key'] = kwargs.pop('img_num')
|
||||
|
||||
# parse_kwargs will extract keyword arguments intended for the TiffFile
|
||||
# class and remove them from the kwargs dictionary in-place
|
||||
tiff_keys = ['multifile', 'multifile_close', 'fastij', 'is_ome']
|
||||
kwargs_tiff = parse_kwargs(kwargs, *tiff_keys)
|
||||
|
||||
# read and return tiff as numpy array
|
||||
with TiffFile(fname, **kwargs_tiff) as tif:
|
||||
return tif.asarray(**kwargs)
|
435
venv/Lib/site-packages/skimage/io/_plugins/util.py
Normal file
435
venv/Lib/site-packages/skimage/io/_plugins/util.py
Normal file
|
@ -0,0 +1,435 @@
|
|||
import numpy as np
|
||||
from . import _colormixer
|
||||
from . import _histograms
|
||||
import threading
|
||||
from ...util import img_as_ubyte
|
||||
|
||||
# utilities to make life easier for plugin writers.
|
||||
|
||||
import multiprocessing
|
||||
CPU_COUNT = multiprocessing.cpu_count()
|
||||
|
||||
|
||||
class GuiLockError(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
|
||||
class WindowManager(object):
|
||||
''' A class to keep track of spawned windows,
|
||||
and make any needed callback once all the windows,
|
||||
are closed.'''
|
||||
def __init__(self):
|
||||
self._windows = []
|
||||
self._callback = None
|
||||
self._callback_args = ()
|
||||
self._callback_kwargs = {}
|
||||
self._gui_lock = False
|
||||
self._guikit = ''
|
||||
|
||||
def _check_locked(self):
|
||||
if not self._gui_lock:
|
||||
raise GuiLockError(\
|
||||
'Must first acquire the gui lock before using this image manager')
|
||||
|
||||
def _exec_callback(self):
|
||||
if self._callback:
|
||||
self._callback(*self._callback_args, **self._callback_kwargs)
|
||||
|
||||
def acquire(self, kit):
|
||||
if self._gui_lock:
|
||||
raise GuiLockError(\
|
||||
'The gui lock can only be acquired by one toolkit per session. \
|
||||
The lock is already acquired by %s' % self._guikit)
|
||||
else:
|
||||
self._gui_lock = True
|
||||
self._guikit = str(kit)
|
||||
|
||||
def _release(self, kit):
|
||||
# releaseing the lock will lose all references to currently
|
||||
# tracked images and the callback.
|
||||
# this function is private for reason!
|
||||
self._check_locked()
|
||||
if str(kit) == self._guikit:
|
||||
self._windows = []
|
||||
self._callback = None
|
||||
self._callback_args = ()
|
||||
self._callback_kwargs = {}
|
||||
self._gui_lock = False
|
||||
self._guikit = ''
|
||||
else:
|
||||
raise RuntimeError('Only the toolkit that owns the lock may '
|
||||
'release it')
|
||||
|
||||
def add_window(self, win):
|
||||
self._check_locked()
|
||||
self._windows.append(win)
|
||||
|
||||
def remove_window(self, win):
|
||||
self._check_locked()
|
||||
try:
|
||||
self._windows.remove(win)
|
||||
except ValueError:
|
||||
print('Unable to find referenced window in tracked windows.')
|
||||
print('Ignoring...')
|
||||
else:
|
||||
if len(self._windows) == 0:
|
||||
self._exec_callback()
|
||||
|
||||
def register_callback(self, cb, *cbargs, **cbkwargs):
|
||||
self._check_locked()
|
||||
self._callback = cb
|
||||
self._callback_args = cbargs
|
||||
self._callback_kwargs = cbkwargs
|
||||
|
||||
def has_windows(self):
|
||||
if len(self._windows) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
window_manager = WindowManager()
|
||||
|
||||
|
||||
def prepare_for_display(npy_img):
|
||||
'''Convert a 2D or 3D numpy array of any dtype into a
|
||||
3D numpy array with dtype uint8. This array will
|
||||
be suitable for use in passing to gui toolkits for
|
||||
image display purposes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
npy_img : ndarray, 2D or 3D
|
||||
The image to convert for display
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray, 3D dtype=np.uint8
|
||||
The converted image. This is guaranteed to be a contiguous array.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If the input image is floating point, it is assumed that the data
|
||||
is in the range of 0.0 - 1.0. No check is made to assert this
|
||||
condition. The image is then scaled to be in the range 0 - 255
|
||||
and then cast to np.uint8
|
||||
|
||||
For all other dtypes, the array is simply cast to np.uint8
|
||||
|
||||
If a 2D array is passed, the single channel is replicated
|
||||
to the 2nd and 3rd channels.
|
||||
|
||||
If the array contains an alpha channel, this channel is
|
||||
ignored.
|
||||
|
||||
'''
|
||||
if npy_img.ndim < 2:
|
||||
raise ValueError('Image must be 2D or 3D array')
|
||||
|
||||
height = npy_img.shape[0]
|
||||
width = npy_img.shape[1]
|
||||
|
||||
out = np.empty((height, width, 3), dtype=np.uint8)
|
||||
npy_img = img_as_ubyte(npy_img)
|
||||
|
||||
if npy_img.ndim == 2 or \
|
||||
(npy_img.ndim == 3 and npy_img.shape[2] == 1):
|
||||
npy_plane = npy_img.reshape((height, width))
|
||||
out[:, :, 0] = npy_plane
|
||||
out[:, :, 1] = npy_plane
|
||||
out[:, :, 2] = npy_plane
|
||||
|
||||
elif npy_img.ndim == 3:
|
||||
if npy_img.shape[2] == 3 or npy_img.shape[2] == 4:
|
||||
out[:, :, :3] = npy_img[:, :, :3]
|
||||
else:
|
||||
raise ValueError('Image must have 1, 3, or 4 channels')
|
||||
|
||||
else:
|
||||
raise ValueError('Image must have 2 or 3 dimensions')
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def histograms(image, nbins):
|
||||
'''Calculate the channel histograms of the current image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray, ndim=3, dtype=np.uint8
|
||||
Input image.
|
||||
nbins : int
|
||||
The number of bins.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : (rcounts, gcounts, bcounts, vcounts)
|
||||
The binned histograms of the RGB channels and intensity values.
|
||||
|
||||
This is a NAIVE histogram routine, meant primarily for fast display.
|
||||
|
||||
'''
|
||||
|
||||
return _histograms.histograms(image, nbins)
|
||||
|
||||
|
||||
class ImgThread(threading.Thread):
|
||||
def __init__(self, func, *args):
|
||||
super(ImgThread, self).__init__()
|
||||
self.func = func
|
||||
self.args = args
|
||||
|
||||
def run(self):
|
||||
self.func(*self.args)
|
||||
|
||||
|
||||
class ThreadDispatch(object):
|
||||
def __init__(self, img, stateimg, func, *args):
|
||||
|
||||
height = img.shape[0]
|
||||
self.cores = CPU_COUNT
|
||||
self.threads = []
|
||||
self.chunks = []
|
||||
|
||||
if self.cores == 1:
|
||||
self.chunks.append((img, stateimg))
|
||||
|
||||
elif self.cores >= 4:
|
||||
self.chunks.append((img[:(height // 4), :, :],
|
||||
stateimg[:(height // 4), :, :]))
|
||||
self.chunks.append((img[(height // 4):(height // 2), :, :],
|
||||
stateimg[(height // 4):(height // 2), :, :]))
|
||||
self.chunks.append((img[(height // 2):(3 * height // 4), :, :],
|
||||
stateimg[(height // 2):(3 * height // 4), :, :]
|
||||
))
|
||||
self.chunks.append((img[(3 * height // 4):, :, :],
|
||||
stateimg[(3 * height // 4):, :, :]))
|
||||
|
||||
# if they don't have 1, or 4 or more, 2 is good.
|
||||
else:
|
||||
self.chunks.append((img[:(height // 2), :, :],
|
||||
stateimg[:(height // 2), :, :]))
|
||||
self.chunks.append((img[(height // 2):, :, :],
|
||||
stateimg[(height // 2):, :, :]))
|
||||
|
||||
for i in range(len(self.chunks)):
|
||||
self.threads.append(ImgThread(func, self.chunks[i][0],
|
||||
self.chunks[i][1], *args))
|
||||
|
||||
def run(self):
|
||||
for t in self.threads:
|
||||
t.start()
|
||||
for t in self.threads:
|
||||
t.join()
|
||||
|
||||
|
||||
class ColorMixer(object):
|
||||
''' a class to manage mixing colors in an image.
|
||||
The input array must be an RGB uint8 image.
|
||||
|
||||
The mixer maintains an original copy of the image,
|
||||
and uses this copy to query the pixel data for operations.
|
||||
It also makes a copy for sharing state across operations.
|
||||
That is, if you add to a channel, and multiply to same channel,
|
||||
the two operations are carried separately and the results
|
||||
averaged together.
|
||||
|
||||
it modifies your array in place. This ensures that if you
|
||||
bust over a threshold, you can always come back down.
|
||||
|
||||
The passed values to a function are always considered
|
||||
absolute. Thus to threshold a channel completely you
|
||||
can do mixer.add(RED, 255). Or to double the intensity
|
||||
of the blue channel: mixer.multiply(BLUE, 2.)
|
||||
|
||||
To reverse these operations, respectively:
|
||||
mixer.add(RED, 0), mixer.multiply(BLUE, 1.)
|
||||
|
||||
The majority of the backend is implemented in Cython,
|
||||
so it should be quite quick.
|
||||
'''
|
||||
|
||||
RED = 0
|
||||
GREEN = 1
|
||||
BLUE = 2
|
||||
|
||||
valid_channels = [RED, GREEN, BLUE]
|
||||
|
||||
def __init__(self, img):
|
||||
if type(img) != np.ndarray:
|
||||
raise ValueError('Image must be a numpy array')
|
||||
if img.dtype != np.uint8:
|
||||
raise ValueError('Image must have dtype uint8')
|
||||
if img.ndim != 3 or img.shape[2] != 3:
|
||||
raise ValueError('Image must be 3 channel MxNx3')
|
||||
|
||||
self.img = img
|
||||
self.origimg = img.copy()
|
||||
self.stateimg = img.copy()
|
||||
|
||||
def get_stateimage(self):
|
||||
return self.stateimg
|
||||
|
||||
def commit_changes(self):
|
||||
self.stateimg[:] = self.img[:]
|
||||
|
||||
def revert(self):
|
||||
self.stateimg[:] = self.origimg[:]
|
||||
self.img[:] = self.stateimg[:]
|
||||
|
||||
def set_to_stateimg(self):
|
||||
self.img[:] = self.stateimg[:]
|
||||
|
||||
def add(self, channel, ammount):
|
||||
'''Add the specified ammount to the specified channel.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
channel : flag
|
||||
the color channel to operate on
|
||||
RED, GREED, or BLUE
|
||||
ammount : integer
|
||||
the ammount of color to add to the channel,
|
||||
can be positive or negative.
|
||||
|
||||
'''
|
||||
if channel not in self.valid_channels:
|
||||
raise ValueError('assert_channel is not a valid channel.')
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.add, channel, ammount)
|
||||
pool.run()
|
||||
|
||||
def multiply(self, channel, ammount):
|
||||
'''Mutliply the indicated channel by the specified value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
channel : flag
|
||||
the color channel to operate on
|
||||
RED, GREED, or BLUE
|
||||
ammount : integer
|
||||
the ammount of color to add to the channel,
|
||||
can be positive or negative.
|
||||
|
||||
'''
|
||||
if channel not in self.valid_channels:
|
||||
raise ValueError('assert_channel is not a valid channel.')
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.multiply, channel, ammount)
|
||||
pool.run()
|
||||
|
||||
def brightness(self, factor, offset):
|
||||
'''Adjust the brightness off an image with an offset and factor.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offset : integer
|
||||
The ammount to add to each channel.
|
||||
factor : float
|
||||
The factor to multiply each channel by.
|
||||
|
||||
result = clip((pixel + offset)*factor)
|
||||
|
||||
'''
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.brightness, factor, offset)
|
||||
pool.run()
|
||||
|
||||
def sigmoid_gamma(self, alpha, beta):
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.sigmoid_gamma, alpha, beta)
|
||||
pool.run()
|
||||
|
||||
def gamma(self, gamma):
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.gamma, gamma)
|
||||
pool.run()
|
||||
|
||||
def hsv_add(self, h_amt, s_amt, v_amt):
|
||||
'''Adjust the H, S, V channels of an image by a constant ammount.
|
||||
This is similar to the add() mixer function, but operates over the
|
||||
entire image at once. Thus all three additive values, H, S, V, must
|
||||
be supplied simultaneously.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h_amt : float
|
||||
The ammount to add to the hue (-180..180)
|
||||
s_amt : float
|
||||
The ammount to add to the saturation (-1..1)
|
||||
v_amt : float
|
||||
The ammount to add to the value (-1..1)
|
||||
|
||||
'''
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.hsv_add, h_amt, s_amt, v_amt)
|
||||
pool.run()
|
||||
|
||||
def hsv_multiply(self, h_amt, s_amt, v_amt):
|
||||
'''Adjust the H, S, V channels of an image by a constant ammount.
|
||||
This is similar to the add() mixer function, but operates over the
|
||||
entire image at once. Thus all three additive values, H, S, V, must
|
||||
be supplied simultaneously.
|
||||
|
||||
Note that since hue is in degrees, it makes no sense to multiply
|
||||
that channel, thus an add operation is performed on the hue. And the
|
||||
values given for h_amt, should be the same as for hsv_add
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h_amt : float
|
||||
The ammount to to add to the hue (-180..180)
|
||||
s_amt : float
|
||||
The ammount to multiply to the saturation (0..1)
|
||||
v_amt : float
|
||||
The ammount to multiply to the value (0..1)
|
||||
|
||||
'''
|
||||
pool = ThreadDispatch(self.img, self.stateimg,
|
||||
_colormixer.hsv_multiply, h_amt, s_amt, v_amt)
|
||||
pool.run()
|
||||
|
||||
def rgb_2_hsv_pixel(self, R, G, B):
|
||||
'''Convert an RGB value to HSV
|
||||
|
||||
Parameters
|
||||
----------
|
||||
R : int
|
||||
Red value
|
||||
G : int
|
||||
Green value
|
||||
B : int
|
||||
Blue value
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : (H, S, V) Floats
|
||||
The HSV values
|
||||
|
||||
'''
|
||||
H, S, V = _colormixer.py_rgb_2_hsv(R, G, B)
|
||||
return (H, S, V)
|
||||
|
||||
def hsv_2_rgb_pixel(self, H, S, V):
|
||||
'''Convert an HSV value to RGB
|
||||
|
||||
Parameters
|
||||
----------
|
||||
H : float
|
||||
Hue value
|
||||
S : float
|
||||
Saturation value
|
||||
V : float
|
||||
Intensity value
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : (R, G, B) ints
|
||||
The RGB values
|
||||
|
||||
'''
|
||||
R, G, B = _colormixer.py_hsv_2_rgb(H, S, V)
|
||||
return (R, G, B)
|
443
venv/Lib/site-packages/skimage/io/collection.py
Normal file
443
venv/Lib/site-packages/skimage/io/collection.py
Normal file
|
@ -0,0 +1,443 @@
|
|||
"""Data structures to hold collections of images, with optional caching."""
|
||||
|
||||
|
||||
import os
|
||||
from glob import glob
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from copy import copy
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from tifffile import TiffFile
|
||||
|
||||
|
||||
__all__ = ['MultiImage', 'ImageCollection', 'concatenate_images',
|
||||
'imread_collection_wrapper']
|
||||
|
||||
|
||||
def concatenate_images(ic):
|
||||
"""Concatenate all images in the image collection into an array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ic : an iterable of images
|
||||
The images to be concatenated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
array_cat : ndarray
|
||||
An array having one more dimension than the images in `ic`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
ImageCollection.concatenate, MultiImage.concatenate
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If images in `ic` don't have identical shapes.
|
||||
|
||||
Notes
|
||||
-----
|
||||
``concatenate_images`` receives any iterable object containing images,
|
||||
including ImageCollection and MultiImage, and returns a NumPy array.
|
||||
"""
|
||||
all_images = [image[np.newaxis, ...] for image in ic]
|
||||
try:
|
||||
array_cat = np.concatenate(all_images)
|
||||
except ValueError:
|
||||
raise ValueError('Image dimensions must agree.')
|
||||
return array_cat
|
||||
|
||||
|
||||
def alphanumeric_key(s):
|
||||
"""Convert string to list of strings and ints that gives intuitive sorting.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
s : string
|
||||
|
||||
Returns
|
||||
-------
|
||||
k : a list of strings and ints
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> alphanumeric_key('z23a')
|
||||
['z', 23, 'a']
|
||||
>>> filenames = ['f9.10.png', 'e10.png', 'f9.9.png', 'f10.10.png',
|
||||
... 'f10.9.png']
|
||||
>>> sorted(filenames)
|
||||
['e10.png', 'f10.10.png', 'f10.9.png', 'f9.10.png', 'f9.9.png']
|
||||
>>> sorted(filenames, key=alphanumeric_key)
|
||||
['e10.png', 'f9.9.png', 'f9.10.png', 'f10.9.png', 'f10.10.png']
|
||||
"""
|
||||
k = [int(c) if c.isdigit() else c for c in re.split('([0-9]+)', s)]
|
||||
return k
|
||||
|
||||
|
||||
def _is_multipattern(input_pattern):
|
||||
"""Helping function. Returns True if pattern contains a tuple, list, or a
|
||||
string separated with os.pathsep."""
|
||||
# Conditions to be accepted by ImageCollection:
|
||||
has_str_ospathsep = (isinstance(input_pattern, str)
|
||||
and os.pathsep in input_pattern)
|
||||
not_a_string = not isinstance(input_pattern, str)
|
||||
has_iterable = isinstance(input_pattern, Sequence)
|
||||
has_strings = all(isinstance(pat, str) for pat in input_pattern)
|
||||
|
||||
is_multipattern = (has_str_ospathsep or
|
||||
(not_a_string
|
||||
and has_iterable
|
||||
and has_strings))
|
||||
return is_multipattern
|
||||
|
||||
|
||||
class ImageCollection(object):
|
||||
"""Load and manage a collection of image files.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
load_pattern : str or list of str
|
||||
Pattern string or list of strings to load. The filename path can be
|
||||
absolute or relative.
|
||||
conserve_memory : bool, optional
|
||||
If True, `ImageCollection` does not keep more than one in memory at a
|
||||
specific time. Otherwise, images will be cached once they are loaded.
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
load_func : callable
|
||||
``imread`` by default. See notes below.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
files : list of str
|
||||
If a pattern string is given for `load_pattern`, this attribute
|
||||
stores the expanded file list. Otherwise, this is equal to
|
||||
`load_pattern`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Note that files are always returned in alphanumerical order. Also note
|
||||
that slicing returns a new ImageCollection, *not* a view into the data.
|
||||
|
||||
ImageCollection can be modified to load images from an arbitrary
|
||||
source by specifying a combination of `load_pattern` and
|
||||
`load_func`. For an ImageCollection ``ic``, ``ic[5]`` uses
|
||||
``load_func(file_pattern[5])`` to load the image.
|
||||
|
||||
Imagine, for example, an ImageCollection that loads every tenth
|
||||
frame from a video file::
|
||||
|
||||
class AVILoader:
|
||||
video_file = 'myvideo.avi'
|
||||
|
||||
def __call__(self, frame):
|
||||
return video_read(self.video_file, frame)
|
||||
|
||||
avi_load = AVILoader()
|
||||
|
||||
frames = range(0, 1000, 10) # 0, 10, 20, ...
|
||||
ic = ImageCollection(frames, load_func=avi_load)
|
||||
|
||||
x = ic[5] # calls avi_load(frames[5]) or equivalently avi_load(50)
|
||||
|
||||
Another use of ``load_func`` would be to convert all images to ``uint8``::
|
||||
|
||||
def imread_convert(f):
|
||||
return imread(f).astype(np.uint8)
|
||||
|
||||
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. In this case, ``load_func``
|
||||
should accept the keyword argument ``img_num``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import skimage.io as io
|
||||
>>> from skimage import data_dir
|
||||
|
||||
>>> coll = io.ImageCollection(data_dir + '/chess*.png')
|
||||
>>> len(coll)
|
||||
2
|
||||
>>> coll[0].shape
|
||||
(200, 200)
|
||||
|
||||
>>> ic = io.ImageCollection(['/tmp/work/*.png', '/tmp/other/*.jpg'])
|
||||
"""
|
||||
def __init__(self, load_pattern, conserve_memory=True, load_func=None,
|
||||
**load_func_kwargs):
|
||||
"""Load and manage a collection of images."""
|
||||
self._files = []
|
||||
if _is_multipattern(load_pattern):
|
||||
if isinstance(load_pattern, str):
|
||||
load_pattern = load_pattern.split(os.pathsep)
|
||||
for pattern in load_pattern:
|
||||
self._files.extend(glob(pattern))
|
||||
self._files = sorted(self._files, key=alphanumeric_key)
|
||||
self._numframes = self._find_images()
|
||||
elif isinstance(load_pattern, str):
|
||||
self._files.extend(glob(load_pattern))
|
||||
self._files = sorted(self._files, key=alphanumeric_key)
|
||||
self._numframes = self._find_images()
|
||||
else:
|
||||
raise TypeError('Invalid pattern as input.')
|
||||
|
||||
if conserve_memory:
|
||||
memory_slots = 1
|
||||
else:
|
||||
memory_slots = self._numframes
|
||||
|
||||
self._conserve_memory = conserve_memory
|
||||
self._cached = None
|
||||
|
||||
if load_func is None:
|
||||
from ._io import imread
|
||||
self.load_func = imread
|
||||
else:
|
||||
self.load_func = load_func
|
||||
|
||||
self.load_func_kwargs = load_func_kwargs
|
||||
self.data = np.empty(memory_slots, dtype=object)
|
||||
|
||||
@property
|
||||
def files(self):
|
||||
return self._files
|
||||
|
||||
@property
|
||||
def conserve_memory(self):
|
||||
return self._conserve_memory
|
||||
|
||||
def _find_images(self):
|
||||
index = []
|
||||
for fname in self._files:
|
||||
if fname.lower().endswith(('.tiff', '.tif')):
|
||||
with open(fname, 'rb') as f:
|
||||
img = TiffFile(f)
|
||||
index += [(fname, i) for i in range(len(img.pages))]
|
||||
else:
|
||||
try:
|
||||
im = Image.open(fname)
|
||||
im.seek(0)
|
||||
except (IOError, OSError):
|
||||
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)
|
||||
|
||||
def __getitem__(self, n):
|
||||
"""Return selected image(s) in the collection.
|
||||
|
||||
Loading is done on demand.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int or slice
|
||||
The image number to be returned, or a slice selecting the images
|
||||
and ordering to be returned in a new ImageCollection.
|
||||
|
||||
Returns
|
||||
-------
|
||||
img : ndarray or ImageCollection.
|
||||
The `n`-th image in the collection, or a new ImageCollection with
|
||||
the selected images.
|
||||
"""
|
||||
if hasattr(n, '__index__'):
|
||||
n = n.__index__()
|
||||
|
||||
if type(n) not in [int, slice]:
|
||||
raise TypeError('slicing must be with an int or slice object')
|
||||
|
||||
if type(n) is int:
|
||||
n = self._check_imgnum(n)
|
||||
idx = n % len(self.data)
|
||||
|
||||
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]
|
||||
if img_num is not None:
|
||||
kwargs['img_num'] = img_num
|
||||
try:
|
||||
self.data[idx] = self.load_func(fname, **kwargs)
|
||||
# Account for functions that do not accept an img_num kwarg
|
||||
except TypeError as e:
|
||||
if "unexpected keyword argument 'img_num'" in str(e):
|
||||
del kwargs['img_num']
|
||||
self.data[idx] = self.load_func(fname, **kwargs)
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
self.data[idx] = self.load_func(self.files[n], **kwargs)
|
||||
self._cached = n
|
||||
|
||||
return self.data[idx]
|
||||
else:
|
||||
# A slice object was provided, so create a new ImageCollection
|
||||
# object. Any loaded image data in the original ImageCollection
|
||||
# will be copied by reference to the new object. Image data
|
||||
# loaded after this creation is not linked.
|
||||
fidx = range(self._numframes)[n]
|
||||
new_ic = copy(self)
|
||||
|
||||
if self._frame_index:
|
||||
new_ic._files = [self._frame_index[i][0] for i in fidx]
|
||||
new_ic._frame_index = [self._frame_index[i] for i in fidx]
|
||||
else:
|
||||
new_ic._files = [self._files[i] for i in fidx]
|
||||
|
||||
new_ic._numframes = len(fidx)
|
||||
|
||||
if self.conserve_memory:
|
||||
if self._cached in fidx:
|
||||
new_ic._cached = fidx.index(self._cached)
|
||||
new_ic.data = np.copy(self.data)
|
||||
else:
|
||||
new_ic.data = np.empty(1, dtype=object)
|
||||
else:
|
||||
new_ic.data = self.data[fidx]
|
||||
return new_ic
|
||||
|
||||
def _check_imgnum(self, n):
|
||||
"""Check that the given image number is valid."""
|
||||
num = self._numframes
|
||||
if -num <= n < num:
|
||||
n = n % num
|
||||
else:
|
||||
raise IndexError("There are only %s images in the collection"
|
||||
% num)
|
||||
return n
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over the images."""
|
||||
for i in range(len(self)):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"""Number of images in collection."""
|
||||
return self._numframes
|
||||
|
||||
def __str__(self):
|
||||
return str(self.files)
|
||||
|
||||
def reload(self, n=None):
|
||||
"""Clear the image cache.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : None or int
|
||||
Clear the cache for this image only. By default, the
|
||||
entire cache is erased.
|
||||
|
||||
"""
|
||||
self.data = np.empty_like(self.data)
|
||||
|
||||
def concatenate(self):
|
||||
"""Concatenate all images in the collection into an array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ar : np.ndarray
|
||||
An array having one more dimension than the images in `self`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
concatenate_images
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If images in the `ImageCollection` don't have identical shapes.
|
||||
"""
|
||||
return concatenate_images(self)
|
||||
|
||||
|
||||
def imread_collection_wrapper(imread):
|
||||
def imread_collection(load_pattern, conserve_memory=True):
|
||||
"""Return an `ImageCollection` from files matching the given pattern.
|
||||
|
||||
Note that files are always stored in alphabetical order. Also note that
|
||||
slicing returns a new ImageCollection, *not* a view into the data.
|
||||
|
||||
See `skimage.io.ImageCollection` for details.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
load_pattern : str or list
|
||||
Pattern glob or filenames to load. The path can be absolute or
|
||||
relative. Multiple patterns should be separated by a colon,
|
||||
e.g. '/tmp/work/*.png:/tmp/other/*.jpg'. Also see
|
||||
implementation notes below.
|
||||
conserve_memory : bool, optional
|
||||
If True, never keep more than one in memory at a specific
|
||||
time. Otherwise, images will be cached once they are loaded.
|
||||
|
||||
"""
|
||||
return ImageCollection(load_pattern, conserve_memory=conserve_memory,
|
||||
load_func=imread)
|
||||
return imread_collection
|
||||
|
||||
|
||||
class MultiImage(ImageCollection):
|
||||
"""A class containing a single multi-frame image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
The complete path to the image file.
|
||||
conserve_memory : bool, optional
|
||||
Whether to conserve memory by only caching a single frame. Default is
|
||||
True.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If ``conserve_memory=True`` the memory footprint can be reduced, however
|
||||
the performance can be affected because frames have to be read from file
|
||||
more often.
|
||||
|
||||
The last accessed frame is cached, all other frames will have to be read
|
||||
from file.
|
||||
|
||||
The current implementation makes use of ``tifffile`` for Tiff files and
|
||||
PIL otherwise.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data_dir
|
||||
|
||||
>>> img = MultiImage(data_dir + '/multipage.tif') # doctest: +SKIP
|
||||
>>> len(img) # doctest: +SKIP
|
||||
2
|
||||
>>> for frame in img: # doctest: +SKIP
|
||||
... print(frame.shape) # doctest: +SKIP
|
||||
(15, 10)
|
||||
(15, 10)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, filename, conserve_memory=True, dtype=None,
|
||||
**imread_kwargs):
|
||||
"""Load a multi-img."""
|
||||
from ._io import imread
|
||||
|
||||
self._filename = filename
|
||||
super(MultiImage, self).__init__(filename, conserve_memory,
|
||||
load_func=imread, **imread_kwargs)
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return self._filename
|
347
venv/Lib/site-packages/skimage/io/manage_plugins.py
Normal file
347
venv/Lib/site-packages/skimage/io/manage_plugins.py
Normal file
|
@ -0,0 +1,347 @@
|
|||
"""Handle image reading, writing and plotting plugins.
|
||||
|
||||
To improve performance, plugins are only loaded as needed. As a result, there
|
||||
can be multiple states for a given plugin:
|
||||
|
||||
available: Defined in an *ini file located in `skimage.io._plugins`.
|
||||
See also `skimage.io.available_plugins`.
|
||||
partial definition: Specified in an *ini file, but not defined in the
|
||||
corresponding plugin module. This will raise an error when loaded.
|
||||
available but not on this system: Defined in `skimage.io._plugins`, but
|
||||
a dependent library (e.g. Qt, PIL) is not available on your system.
|
||||
This will raise an error when loaded.
|
||||
loaded: The real availability is determined when it's explicitly loaded,
|
||||
either because it's one of the default plugins, or because it's
|
||||
loaded explicitly by the user.
|
||||
|
||||
"""
|
||||
import os.path
|
||||
import warnings
|
||||
from configparser import ConfigParser
|
||||
from glob import glob
|
||||
|
||||
from .collection import imread_collection_wrapper
|
||||
|
||||
__all__ = ['use_plugin', 'call_plugin', 'plugin_info', 'plugin_order',
|
||||
'reset_plugins', 'find_available_plugins', 'available_plugins']
|
||||
|
||||
# The plugin store will save a list of *loaded* io functions for each io type
|
||||
# (e.g. 'imread', 'imsave', etc.). Plugins are loaded as requested.
|
||||
plugin_store = None
|
||||
# Dictionary mapping plugin names to a list of functions they provide.
|
||||
plugin_provides = {}
|
||||
# The module names for the plugins in `skimage.io._plugins`.
|
||||
plugin_module_name = {}
|
||||
# Meta-data about plugins provided by *.ini files.
|
||||
plugin_meta_data = {}
|
||||
# For each plugin type, default to the first available plugin as defined by
|
||||
# the following preferences.
|
||||
preferred_plugins = {
|
||||
# Default plugins for all types (overridden by specific types below).
|
||||
'all': ['imageio', 'pil', 'matplotlib', 'qt'],
|
||||
'imshow': ['matplotlib'],
|
||||
'imshow_collection': ['matplotlib']
|
||||
}
|
||||
|
||||
|
||||
def _clear_plugins():
|
||||
"""Clear the plugin state to the default, i.e., where no plugins are loaded
|
||||
|
||||
"""
|
||||
global plugin_store
|
||||
plugin_store = {'imread': [],
|
||||
'imsave': [],
|
||||
'imshow': [],
|
||||
'imread_collection': [],
|
||||
'imshow_collection': [],
|
||||
'_app_show': []}
|
||||
|
||||
|
||||
_clear_plugins()
|
||||
|
||||
|
||||
def _load_preferred_plugins():
|
||||
# Load preferred plugin for each io function.
|
||||
io_types = ['imsave', 'imshow', 'imread_collection', 'imshow_collection',
|
||||
'imread']
|
||||
for p_type in io_types:
|
||||
_set_plugin(p_type, preferred_plugins['all'])
|
||||
|
||||
plugin_types = (p for p in preferred_plugins.keys() if p != 'all')
|
||||
for p_type in plugin_types:
|
||||
_set_plugin(p_type, preferred_plugins[p_type])
|
||||
|
||||
|
||||
def _set_plugin(plugin_type, plugin_list):
|
||||
for plugin in plugin_list:
|
||||
if plugin not in available_plugins:
|
||||
continue
|
||||
try:
|
||||
use_plugin(plugin, kind=plugin_type)
|
||||
break
|
||||
except (ImportError, RuntimeError, OSError):
|
||||
pass
|
||||
|
||||
|
||||
def reset_plugins():
|
||||
_clear_plugins()
|
||||
_load_preferred_plugins()
|
||||
|
||||
|
||||
def _parse_config_file(filename):
|
||||
"""Return plugin name and meta-data dict from plugin config file."""
|
||||
parser = ConfigParser()
|
||||
parser.read(filename)
|
||||
name = parser.sections()[0]
|
||||
|
||||
meta_data = {}
|
||||
for opt in parser.options(name):
|
||||
meta_data[opt] = parser.get(name, opt)
|
||||
|
||||
return name, meta_data
|
||||
|
||||
|
||||
def _scan_plugins():
|
||||
"""Scan the plugins directory for .ini files and parse them
|
||||
to gather plugin meta-data.
|
||||
|
||||
"""
|
||||
pd = os.path.dirname(__file__)
|
||||
config_files = glob(os.path.join(pd, '_plugins', '*.ini'))
|
||||
|
||||
for filename in config_files:
|
||||
name, meta_data = _parse_config_file(filename)
|
||||
if 'provides' not in meta_data:
|
||||
warnings.warn(f'file {filename} not recognized as a scikit-image io plugin, skipping.')
|
||||
continue
|
||||
plugin_meta_data[name] = meta_data
|
||||
provides = [s.strip() for s in meta_data['provides'].split(',')]
|
||||
valid_provides = [p for p in provides if p in plugin_store]
|
||||
|
||||
for p in provides:
|
||||
if p not in plugin_store:
|
||||
print("Plugin `%s` wants to provide non-existent `%s`."
|
||||
" Ignoring." % (name, p))
|
||||
|
||||
# Add plugins that provide 'imread' as provider of 'imread_collection'.
|
||||
need_to_add_collection = ('imread_collection' not in valid_provides and
|
||||
'imread' in valid_provides)
|
||||
if need_to_add_collection:
|
||||
valid_provides.append('imread_collection')
|
||||
|
||||
plugin_provides[name] = valid_provides
|
||||
|
||||
plugin_module_name[name] = os.path.basename(filename)[:-4]
|
||||
|
||||
|
||||
_scan_plugins()
|
||||
|
||||
|
||||
def find_available_plugins(loaded=False):
|
||||
"""List available plugins.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
loaded : bool
|
||||
If True, show only those plugins currently loaded. By default,
|
||||
all plugins are shown.
|
||||
|
||||
Returns
|
||||
-------
|
||||
p : dict
|
||||
Dictionary with plugin names as keys and exposed functions as
|
||||
values.
|
||||
|
||||
"""
|
||||
active_plugins = set()
|
||||
for plugin_func in plugin_store.values():
|
||||
for plugin, func in plugin_func:
|
||||
active_plugins.add(plugin)
|
||||
|
||||
d = {}
|
||||
for plugin in plugin_provides:
|
||||
if not loaded or plugin in active_plugins:
|
||||
d[plugin] = [f for f in plugin_provides[plugin]
|
||||
if not f.startswith('_')]
|
||||
|
||||
return d
|
||||
|
||||
|
||||
available_plugins = find_available_plugins()
|
||||
|
||||
|
||||
def call_plugin(kind, *args, **kwargs):
|
||||
"""Find the appropriate plugin of 'kind' and execute it.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
kind : {'imshow', 'imsave', 'imread', 'imread_collection'}
|
||||
Function to look up.
|
||||
plugin : str, optional
|
||||
Plugin to load. Defaults to None, in which case the first
|
||||
matching plugin is used.
|
||||
*args, **kwargs : arguments and keyword arguments
|
||||
Passed to the plugin function.
|
||||
|
||||
"""
|
||||
if kind not in plugin_store:
|
||||
raise ValueError('Invalid function (%s) requested.' % kind)
|
||||
|
||||
plugin_funcs = plugin_store[kind]
|
||||
if len(plugin_funcs) == 0:
|
||||
msg = ("No suitable plugin registered for %s.\n\n"
|
||||
"You may load I/O plugins with the `skimage.io.use_plugin` "
|
||||
"command. A list of all available plugins are shown in the "
|
||||
"`skimage.io` docstring.")
|
||||
raise RuntimeError(msg % kind)
|
||||
|
||||
plugin = kwargs.pop('plugin', None)
|
||||
if plugin is None:
|
||||
_, func = plugin_funcs[0]
|
||||
else:
|
||||
_load(plugin)
|
||||
try:
|
||||
func = [f for (p, f) in plugin_funcs if p == plugin][0]
|
||||
except IndexError:
|
||||
raise RuntimeError('Could not find the plugin "%s" for %s.' %
|
||||
(plugin, kind))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
|
||||
def use_plugin(name, kind=None):
|
||||
"""Set the default plugin for a specified operation. The plugin
|
||||
will be loaded if it hasn't been already.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of plugin.
|
||||
kind : {'imsave', 'imread', 'imshow', 'imread_collection', 'imshow_collection'}, optional
|
||||
Set the plugin for this function. By default,
|
||||
the plugin is set for all functions.
|
||||
|
||||
See Also
|
||||
--------
|
||||
available_plugins : List of available plugins
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
To use Matplotlib as the default image reader, you would write:
|
||||
|
||||
>>> from skimage import io
|
||||
>>> io.use_plugin('matplotlib', 'imread')
|
||||
|
||||
To see a list of available plugins run ``io.available_plugins``. Note that
|
||||
this lists plugins that are defined, but the full list may not be usable
|
||||
if your system does not have the required libraries installed.
|
||||
|
||||
"""
|
||||
if kind is None:
|
||||
kind = plugin_store.keys()
|
||||
else:
|
||||
if kind not in plugin_provides[name]:
|
||||
raise RuntimeError("Plugin %s does not support `%s`." %
|
||||
(name, kind))
|
||||
|
||||
if kind == 'imshow':
|
||||
kind = [kind, '_app_show']
|
||||
else:
|
||||
kind = [kind]
|
||||
|
||||
_load(name)
|
||||
|
||||
for k in kind:
|
||||
if k not in plugin_store:
|
||||
raise RuntimeError("'%s' is not a known plugin function." % k)
|
||||
|
||||
funcs = plugin_store[k]
|
||||
|
||||
# Shuffle the plugins so that the requested plugin stands first
|
||||
# in line
|
||||
funcs = [(n, f) for (n, f) in funcs if n == name] + \
|
||||
[(n, f) for (n, f) in funcs if n != name]
|
||||
|
||||
plugin_store[k] = funcs
|
||||
|
||||
|
||||
def _inject_imread_collection_if_needed(module):
|
||||
"""Add `imread_collection` to module if not already present."""
|
||||
if not hasattr(module, 'imread_collection') and hasattr(module, 'imread'):
|
||||
imread = getattr(module, 'imread')
|
||||
func = imread_collection_wrapper(imread)
|
||||
setattr(module, 'imread_collection', func)
|
||||
|
||||
|
||||
def _load(plugin):
|
||||
"""Load the given plugin.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
plugin : str
|
||||
Name of plugin to load.
|
||||
|
||||
See Also
|
||||
--------
|
||||
plugins : List of available plugins
|
||||
|
||||
"""
|
||||
if plugin in find_available_plugins(loaded=True):
|
||||
return
|
||||
if plugin not in plugin_module_name:
|
||||
raise ValueError("Plugin %s not found." % plugin)
|
||||
else:
|
||||
modname = plugin_module_name[plugin]
|
||||
plugin_module = __import__('skimage.io._plugins.' + modname,
|
||||
fromlist=[modname])
|
||||
|
||||
provides = plugin_provides[plugin]
|
||||
for p in provides:
|
||||
if p == 'imread_collection':
|
||||
_inject_imread_collection_if_needed(plugin_module)
|
||||
elif not hasattr(plugin_module, p):
|
||||
print("Plugin %s does not provide %s as advertised. Ignoring." %
|
||||
(plugin, p))
|
||||
continue
|
||||
|
||||
store = plugin_store[p]
|
||||
func = getattr(plugin_module, p)
|
||||
if not (plugin, func) in store:
|
||||
store.append((plugin, func))
|
||||
|
||||
|
||||
def plugin_info(plugin):
|
||||
"""Return plugin meta-data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
plugin : str
|
||||
Name of plugin.
|
||||
|
||||
Returns
|
||||
-------
|
||||
m : dict
|
||||
Meta data as specified in plugin ``.ini``.
|
||||
|
||||
"""
|
||||
try:
|
||||
return plugin_meta_data[plugin]
|
||||
except KeyError:
|
||||
raise ValueError('No information on plugin "%s"' % plugin)
|
||||
|
||||
|
||||
def plugin_order():
|
||||
"""Return the currently preferred plugin order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
p : dict
|
||||
Dictionary of preferred plugin order, with function name as key and
|
||||
plugins (in order of preference) as value.
|
||||
|
||||
"""
|
||||
p = {}
|
||||
for func in plugin_store:
|
||||
p[func] = [plugin_name for (plugin_name, f) in plugin_store[func]]
|
||||
return p
|
40
venv/Lib/site-packages/skimage/io/setup.py
Normal file
40
venv/Lib/site-packages/skimage/io/setup.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from skimage._build import cython
|
||||
|
||||
import os.path
|
||||
|
||||
base_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def configuration(parent_package='', top_path=None):
|
||||
from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs
|
||||
|
||||
config = Configuration('io', parent_package, top_path)
|
||||
config.add_data_files('_plugins/*.ini')
|
||||
|
||||
# This function tries to create C files from the given .pyx files. If
|
||||
# it fails, we build the checked-in .c files.
|
||||
cython(['_plugins/_colormixer.pyx', '_plugins/_histograms.pyx'],
|
||||
working_path=base_path)
|
||||
|
||||
config.add_extension('_plugins._colormixer',
|
||||
sources=['_plugins/_colormixer.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
|
||||
config.add_extension('_plugins._histograms',
|
||||
sources=['_plugins/_histograms.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
|
||||
return config
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from numpy.distutils.core import setup
|
||||
setup(maintainer='scikit-image Developers',
|
||||
maintainer_email='scikit-image@python.org',
|
||||
description='Image I/O Routines',
|
||||
url='https://github.com/scikit-image/scikit-image',
|
||||
license='Modified BSD',
|
||||
**(configuration(top_path='').todict())
|
||||
)
|
81
venv/Lib/site-packages/skimage/io/sift.py
Normal file
81
venv/Lib/site-packages/skimage/io/sift.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
import numpy as np
|
||||
|
||||
__all__ = ['load_sift', 'load_surf']
|
||||
|
||||
|
||||
def _sift_read(filelike, mode='SIFT'):
|
||||
"""Read SIFT or SURF features from externally generated file.
|
||||
|
||||
This routine reads SIFT or SURF files generated by binary utilities from
|
||||
http://people.cs.ubc.ca/~lowe/keypoints/ and
|
||||
http://www.vision.ee.ethz.ch/~surf/.
|
||||
|
||||
This routine *does not* generate SIFT/SURF features from an image. These
|
||||
algorithms are patent encumbered. Please use `skimage.feature.CENSURE`
|
||||
instead.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filelike : string or open file
|
||||
Input file generated by the feature detectors from
|
||||
http://people.cs.ubc.ca/~lowe/keypoints/ or
|
||||
http://www.vision.ee.ethz.ch/~surf/ .
|
||||
mode : {'SIFT', 'SURF'}, optional
|
||||
Kind of descriptor used to generate `filelike`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : record array with fields
|
||||
- row: int
|
||||
row position of feature
|
||||
- column: int
|
||||
column position of feature
|
||||
- scale: float
|
||||
feature scale
|
||||
- orientation: float
|
||||
feature orientation
|
||||
- data: array
|
||||
feature values
|
||||
|
||||
"""
|
||||
if isinstance(filelike, str):
|
||||
f = open(filelike, 'r')
|
||||
filelike_is_str = True
|
||||
else:
|
||||
f = filelike
|
||||
filelike_is_str = False
|
||||
|
||||
if mode == 'SIFT':
|
||||
nr_features, feature_len = map(int, f.readline().split())
|
||||
datatype = np.dtype([('row', float), ('column', float),
|
||||
('scale', float), ('orientation', float),
|
||||
('data', (float, feature_len))])
|
||||
else:
|
||||
mode = 'SURF'
|
||||
feature_len = int(f.readline()) - 1
|
||||
nr_features = int(f.readline())
|
||||
datatype = np.dtype([('column', float), ('row', float),
|
||||
('second_moment', (float, 3)),
|
||||
('sign', float), ('data', (float, feature_len))])
|
||||
|
||||
data = np.fromfile(f, sep=' ')
|
||||
if data.size != nr_features * datatype.itemsize / np.dtype(float).itemsize:
|
||||
raise IOError("Invalid {} feature file.".format(mode))
|
||||
|
||||
# If `filelike` is passed to the function as filename - close the file
|
||||
if filelike_is_str:
|
||||
f.close()
|
||||
|
||||
return data.view(datatype)
|
||||
|
||||
|
||||
def load_sift(f):
|
||||
return _sift_read(f, mode='SIFT')
|
||||
|
||||
|
||||
def load_surf(f):
|
||||
return _sift_read(f, mode='SURF')
|
||||
|
||||
|
||||
load_sift.__doc__ = _sift_read.__doc__
|
||||
load_surf.__doc__ = _sift_read.__doc__
|
9
venv/Lib/site-packages/skimage/io/tests/__init__.py
Normal file
9
venv/Lib/site-packages/skimage/io/tests/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from ..._shared.testing import setup_test, teardown_test
|
||||
|
||||
|
||||
def setup():
|
||||
setup_test()
|
||||
|
||||
|
||||
def teardown():
|
||||
teardown_test()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
111
venv/Lib/site-packages/skimage/io/tests/test_collection.py
Normal file
111
venv/Lib/site-packages/skimage/io/tests/test_collection.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
import os
|
||||
|
||||
import numpy as np
|
||||
from skimage import data_dir
|
||||
from skimage.io.collection import ImageCollection, alphanumeric_key
|
||||
from skimage.io import reset_plugins
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_equal, assert_allclose, TestCase
|
||||
|
||||
|
||||
def test_string_split():
|
||||
test_string = 'z23a'
|
||||
test_str_result = ['z', 23, 'a']
|
||||
assert_equal(alphanumeric_key(test_string), test_str_result)
|
||||
|
||||
|
||||
def test_string_sort():
|
||||
filenames = ['f9.10.png', 'f9.9.png', 'f10.10.png', 'f10.9.png',
|
||||
'e9.png', 'e10.png', 'em.png']
|
||||
sorted_filenames = ['e9.png', 'e10.png', 'em.png', 'f9.9.png',
|
||||
'f9.10.png', 'f10.9.png', 'f10.10.png']
|
||||
sorted_filenames = sorted(filenames, key=alphanumeric_key)
|
||||
assert_equal(sorted_filenames, sorted_filenames)
|
||||
|
||||
|
||||
def test_imagecollection_input():
|
||||
"""Test function for ImageCollection. The new behavior (implemented
|
||||
in 0.16) allows the `pattern` argument to accept a list of strings
|
||||
as the input.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If correct, `images` will receive three images.
|
||||
"""
|
||||
# Ensure that these images are part of the legacy datasets
|
||||
# this means they will always be available in the user's install
|
||||
# regarless of the availability of pooch
|
||||
pattern = [os.path.join(data_dir, pic)
|
||||
for pic in ['coffee.png',
|
||||
'chessboard_GRAY.png',
|
||||
'rocket.jpg']]
|
||||
images = ImageCollection(pattern)
|
||||
assert len(images) == 3
|
||||
|
||||
|
||||
class TestImageCollection(TestCase):
|
||||
pattern = [os.path.join(data_dir, pic)
|
||||
for pic in ['camera.png', 'color.png']]
|
||||
|
||||
pattern_matched = [os.path.join(data_dir, pic)
|
||||
for pic in ['camera.png', 'moon.png']]
|
||||
|
||||
def setUp(self):
|
||||
reset_plugins()
|
||||
# Generic image collection with images of different shapes.
|
||||
self.images = ImageCollection(self.pattern)
|
||||
# Image collection with images having shapes that match.
|
||||
self.images_matched = ImageCollection(self.pattern_matched)
|
||||
|
||||
def test_len(self):
|
||||
assert len(self.images) == 2
|
||||
|
||||
def test_getitem(self):
|
||||
num = len(self.images)
|
||||
for i in range(-num, num):
|
||||
assert isinstance(self.images[i], np.ndarray)
|
||||
assert_allclose(self.images[0],
|
||||
self.images[-num])
|
||||
|
||||
def return_img(n):
|
||||
return self.images[n]
|
||||
with testing.raises(IndexError):
|
||||
return_img(num)
|
||||
with testing.raises(IndexError):
|
||||
return_img(-num - 1)
|
||||
|
||||
def test_slicing(self):
|
||||
assert type(self.images[:]) is ImageCollection
|
||||
assert len(self.images[:]) == 2
|
||||
assert len(self.images[:1]) == 1
|
||||
assert len(self.images[1:]) == 1
|
||||
assert_allclose(self.images[0], self.images[:1][0])
|
||||
assert_allclose(self.images[1], self.images[1:][0])
|
||||
assert_allclose(self.images[1], self.images[::-1][0])
|
||||
assert_allclose(self.images[0], self.images[::-1][1])
|
||||
|
||||
def test_files_property(self):
|
||||
assert isinstance(self.images.files, list)
|
||||
|
||||
def set_files(f):
|
||||
self.images.files = f
|
||||
with testing.raises(AttributeError):
|
||||
set_files('newfiles')
|
||||
|
||||
def test_custom_load_func(self):
|
||||
|
||||
def load_fn(x):
|
||||
return x
|
||||
|
||||
ic = ImageCollection(os.pathsep.join(self.pattern), load_func=load_fn)
|
||||
assert_equal(ic[0], self.pattern[0])
|
||||
|
||||
def test_concatenate(self):
|
||||
array = self.images_matched.concatenate()
|
||||
expected_shape = (len(self.images_matched),) + self.images[0].shape
|
||||
assert_equal(array.shape, expected_shape)
|
||||
|
||||
def test_concatenate_mismatched_image_shapes(self):
|
||||
with testing.raises(ValueError):
|
||||
self.images.concatenate()
|
136
venv/Lib/site-packages/skimage/io/tests/test_colormixer.py
Normal file
136
venv/Lib/site-packages/skimage/io/tests/test_colormixer.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
import numpy as np
|
||||
import skimage.io._plugins._colormixer as cm
|
||||
|
||||
from skimage._shared.testing import (assert_array_equal, assert_almost_equal,
|
||||
assert_equal, assert_array_almost_equal)
|
||||
|
||||
|
||||
class ColorMixerTest(object):
|
||||
def setup(self):
|
||||
self.state = np.full((18, 33, 3), 200, dtype=np.uint8)
|
||||
self.img = np.zeros_like(self.state)
|
||||
|
||||
def test_basic(self):
|
||||
self.op(self.img, self.state, 0, self.positive)
|
||||
assert_array_equal(self.img[..., 0],
|
||||
self.py_op(self.state[..., 0], self.positive))
|
||||
|
||||
def test_clip(self):
|
||||
self.op(self.img, self.state, 0, self.positive_clip)
|
||||
assert_array_equal(self.img[..., 0],
|
||||
np.full_like(self.img[..., 0], 255))
|
||||
|
||||
def test_negative(self):
|
||||
self.op(self.img, self.state, 0, self.negative)
|
||||
assert_array_equal(self.img[..., 0],
|
||||
self.py_op(self.state[..., 0], self.negative))
|
||||
|
||||
def test_negative_clip(self):
|
||||
self.op(self.img, self.state, 0, self.negative_clip)
|
||||
assert_array_equal(self.img[..., 0],
|
||||
np.zeros_like(self.img[..., 0]))
|
||||
|
||||
|
||||
class TestColorMixerAdd(ColorMixerTest):
|
||||
op = staticmethod(cm.add)
|
||||
py_op = np.add
|
||||
positive = 50
|
||||
positive_clip = 56
|
||||
negative = -50
|
||||
negative_clip = -220
|
||||
|
||||
|
||||
class TestColorMixerMul(ColorMixerTest):
|
||||
op = staticmethod(cm.multiply)
|
||||
py_op = np.multiply
|
||||
positive = 1.2
|
||||
positive_clip = 2
|
||||
negative = 0.5
|
||||
negative_clip = -0.5
|
||||
|
||||
|
||||
class TestColorMixerBright(object):
|
||||
|
||||
def setup(self):
|
||||
self.state = np.ones((18, 33, 3), dtype=np.uint8) * 200
|
||||
self.img = np.zeros_like(self.state)
|
||||
|
||||
def test_brightness_pos(self):
|
||||
cm.brightness(self.img, self.state, 1.25, 1)
|
||||
assert_array_equal(self.img, np.ones_like(self.img) * 251)
|
||||
|
||||
def test_brightness_neg(self):
|
||||
cm.brightness(self.img, self.state, 0.5, -50)
|
||||
assert_array_equal(self.img, np.ones_like(self.img) * 50)
|
||||
|
||||
def test_brightness_pos_clip(self):
|
||||
cm.brightness(self.img, self.state, 2, 0)
|
||||
assert_array_equal(self.img, np.ones_like(self.img) * 255)
|
||||
|
||||
def test_brightness_neg_clip(self):
|
||||
cm.brightness(self.img, self.state, 0, 0)
|
||||
assert_array_equal(self.img, np.zeros_like(self.img))
|
||||
|
||||
|
||||
class TestColorMixer(object):
|
||||
|
||||
def setup(self):
|
||||
self.state = np.ones((18, 33, 3), dtype=np.uint8) * 50
|
||||
self.img = np.zeros_like(self.state)
|
||||
|
||||
def test_sigmoid(self):
|
||||
import math
|
||||
alpha = 1.5
|
||||
beta = 1.5
|
||||
c1 = 1 / (1 + math.exp(beta))
|
||||
c2 = 1 / (1 + math.exp(beta - alpha)) - c1
|
||||
state = self.state / 255.
|
||||
cm.sigmoid_gamma(self.img, self.state, alpha, beta)
|
||||
img = 1 / (1 + np.exp(beta - state * alpha))
|
||||
img = np.asarray((img - c1) / c2 * 255, dtype='uint8')
|
||||
assert_almost_equal(img, self.img)
|
||||
|
||||
def test_gamma(self):
|
||||
gamma = 1.5
|
||||
cm.gamma(self.img, self.state, gamma)
|
||||
img = np.asarray(((self.state / 255.)**(1 / gamma)) * 255,
|
||||
dtype='uint8')
|
||||
assert_array_almost_equal(img, self.img)
|
||||
|
||||
def test_rgb_2_hsv(self):
|
||||
r = 255
|
||||
g = 0
|
||||
b = 0
|
||||
h, s, v = cm.py_rgb_2_hsv(r, g, b)
|
||||
assert_almost_equal(np.array([h]), np.array([0]))
|
||||
assert_almost_equal(np.array([s]), np.array([1]))
|
||||
assert_almost_equal(np.array([v]), np.array([1]))
|
||||
|
||||
def test_hsv_2_rgb(self):
|
||||
h = 0
|
||||
s = 1
|
||||
v = 1
|
||||
r, g, b = cm.py_hsv_2_rgb(h, s, v)
|
||||
assert_almost_equal(np.array([r]), np.array([255]))
|
||||
assert_almost_equal(np.array([g]), np.array([0]))
|
||||
assert_almost_equal(np.array([b]), np.array([0]))
|
||||
|
||||
def test_hsv_add(self):
|
||||
cm.hsv_add(self.img, self.state, 360, 0, 0)
|
||||
assert_almost_equal(self.img, self.state)
|
||||
|
||||
def test_hsv_add_clip_neg(self):
|
||||
cm.hsv_add(self.img, self.state, 0, 0, -1)
|
||||
assert_equal(self.img, np.zeros_like(self.state))
|
||||
|
||||
def test_hsv_add_clip_pos(self):
|
||||
cm.hsv_add(self.img, self.state, 0, 0, 1)
|
||||
assert_equal(self.img, np.ones_like(self.state) * 255)
|
||||
|
||||
def test_hsv_mul(self):
|
||||
cm.hsv_multiply(self.img, self.state, 360, 1, 1)
|
||||
assert_almost_equal(self.img, self.state)
|
||||
|
||||
def test_hsv_mul_clip_neg(self):
|
||||
cm.hsv_multiply(self.img, self.state, 0, 0, 0)
|
||||
assert_equal(self.img, np.zeros_like(self.state))
|
35
venv/Lib/site-packages/skimage/io/tests/test_fits.py
Normal file
35
venv/Lib/site-packages/skimage/io/tests/test_fits.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import os.path
|
||||
import numpy as np
|
||||
import skimage.io as io
|
||||
from skimage._shared import testing
|
||||
|
||||
testing.pytest.importorskip('astropy')
|
||||
from astropy.io import fits
|
||||
import skimage.io._plugins.fits_plugin as fplug
|
||||
|
||||
|
||||
def test_fits_plugin_import():
|
||||
# Make sure we get an import exception if Astropy isn't there
|
||||
# (not sure how useful this is, but it ensures there isn't some other
|
||||
# error when trying to load the plugin)
|
||||
try:
|
||||
io.use_plugin('fits')
|
||||
except ImportError:
|
||||
raise()
|
||||
|
||||
|
||||
def teardown():
|
||||
io.reset_plugins()
|
||||
|
||||
|
||||
def _same_ImageCollection(collection1, collection2):
|
||||
"""
|
||||
Ancillary function to compare two ImageCollection objects, checking that
|
||||
their constituent arrays are equal.
|
||||
"""
|
||||
if len(collection1) != len(collection2):
|
||||
return False
|
||||
for ext1, ext2 in zip(collection1, collection2):
|
||||
if not np.all(ext1 == ext2):
|
||||
return False
|
||||
return True
|
25
venv/Lib/site-packages/skimage/io/tests/test_histograms.py
Normal file
25
venv/Lib/site-packages/skimage/io/tests/test_histograms.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
import numpy as np
|
||||
from skimage.io._plugins._histograms import histograms
|
||||
|
||||
from skimage._shared.testing import assert_array_equal, assert_equal, TestCase
|
||||
|
||||
|
||||
class TestHistogram(TestCase):
|
||||
def test_basic(self):
|
||||
img = np.ones((50, 50, 3), dtype=np.uint8)
|
||||
r, g, b, v = histograms(img, 255)
|
||||
|
||||
for band in (r, g, b, v):
|
||||
yield assert_equal, band.sum(), 50 * 50
|
||||
|
||||
def test_counts(self):
|
||||
channel = np.arange(255).reshape(51, 5)
|
||||
img = np.empty((51, 5, 3), dtype='uint8')
|
||||
img[:, :, 0] = channel
|
||||
img[:, :, 1] = channel
|
||||
img[:, :, 2] = channel
|
||||
r, g, b, v = histograms(img, 255)
|
||||
assert_array_equal(r, g)
|
||||
assert_array_equal(r, b)
|
||||
assert_array_equal(r, v)
|
||||
assert_array_equal(r, np.ones(255))
|
79
venv/Lib/site-packages/skimage/io/tests/test_imageio.py
Normal file
79
venv/Lib/site-packages/skimage/io/tests/test_imageio.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from skimage.io import imread, imsave, use_plugin, reset_plugins
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_array_almost_equal, TestCase, fetch
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
|
||||
def setup():
|
||||
use_plugin('imageio')
|
||||
|
||||
|
||||
def teardown():
|
||||
reset_plugins()
|
||||
|
||||
|
||||
def test_imageio_as_gray():
|
||||
|
||||
img = imread(fetch('data/color.png'), as_gray=True)
|
||||
assert img.ndim == 2
|
||||
assert img.dtype == np.float64
|
||||
img = imread(fetch('data/camera.png'), as_gray=True)
|
||||
# check that conversion does not happen for a gray image
|
||||
assert np.sctype2char(img.dtype) in np.typecodes['AllInteger']
|
||||
|
||||
|
||||
def test_imageio_palette():
|
||||
img = imread(fetch('data/palette_color.png'))
|
||||
assert img.ndim == 3
|
||||
|
||||
|
||||
def test_imageio_truncated_jpg():
|
||||
# imageio>2.0 uses Pillow / PIL to try and load the file.
|
||||
# Oddly, PIL explicitly raises a SyntaxError when the file read fails.
|
||||
with testing.raises(SyntaxError):
|
||||
imread(fetch('data/truncated.jpg'))
|
||||
|
||||
|
||||
class TestSave(TestCase):
|
||||
|
||||
def roundtrip(self, x, scaling=1):
|
||||
f = NamedTemporaryFile(suffix='.png')
|
||||
fname = f.name
|
||||
f.close()
|
||||
imsave(fname, x)
|
||||
y = imread(fname)
|
||||
|
||||
assert_array_almost_equal((x * scaling).astype(np.int32), y)
|
||||
|
||||
def test_imsave_roundtrip(self):
|
||||
dtype = np.uint8
|
||||
np.random.seed(0)
|
||||
for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]:
|
||||
x = np.ones(shape, dtype=dtype) * np.random.rand(*shape)
|
||||
|
||||
if np.issubdtype(dtype, np.floating):
|
||||
yield self.roundtrip, x, 255
|
||||
else:
|
||||
x = (x * 255).astype(dtype)
|
||||
yield self.roundtrip, x
|
||||
|
||||
def test_bool_array_save(self):
|
||||
f = NamedTemporaryFile(suffix='.png')
|
||||
fname = f.name
|
||||
f.close()
|
||||
with expected_warnings(['.* is a boolean image']):
|
||||
a = np.zeros((5, 5), bool)
|
||||
a[2, 2] = True
|
||||
imsave(fname, a)
|
||||
|
||||
|
||||
def test_return_class():
|
||||
testing.assert_equal(
|
||||
type(imread(fetch('data/color.png'))),
|
||||
np.ndarray
|
||||
)
|
71
venv/Lib/site-packages/skimage/io/tests/test_imread.py
Normal file
71
venv/Lib/site-packages/skimage/io/tests/test_imread.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import numpy as np
|
||||
from skimage import data, io
|
||||
from skimage.io import imread, imsave, use_plugin, reset_plugins
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import (TestCase, assert_array_equal,
|
||||
assert_array_almost_equal, fetch)
|
||||
|
||||
from pytest import importorskip
|
||||
|
||||
importorskip('imread')
|
||||
|
||||
def setup():
|
||||
use_plugin('imread')
|
||||
|
||||
|
||||
def teardown():
|
||||
reset_plugins()
|
||||
|
||||
|
||||
def test_imread_as_gray():
|
||||
img = imread(fetch('data/color.png'), as_gray=True)
|
||||
assert img.ndim == 2
|
||||
assert img.dtype == np.float64
|
||||
img = imread(fetch('data/camera.png'), as_gray=True)
|
||||
# check that conversion does not happen for a gray image
|
||||
assert np.sctype2char(img.dtype) in np.typecodes['AllInteger']
|
||||
|
||||
|
||||
def test_imread_palette():
|
||||
img = imread(fetch('data/palette_color.png'))
|
||||
assert img.ndim == 3
|
||||
|
||||
|
||||
def test_imread_truncated_jpg():
|
||||
with testing.raises(RuntimeError):
|
||||
io.imread(fetch('data/truncated.jpg'))
|
||||
|
||||
|
||||
def test_bilevel():
|
||||
expected = np.zeros((10, 10), bool)
|
||||
expected[::2] = 1
|
||||
|
||||
img = imread(fetch('data/checker_bilevel.png'))
|
||||
assert_array_equal(img.astype(bool), expected)
|
||||
|
||||
|
||||
class TestSave(TestCase):
|
||||
def roundtrip(self, x, scaling=1):
|
||||
f = NamedTemporaryFile(suffix='.png')
|
||||
fname = f.name
|
||||
f.close()
|
||||
imsave(fname, x)
|
||||
y = imread(fname)
|
||||
|
||||
assert_array_almost_equal((x * scaling).astype(np.int32), y)
|
||||
|
||||
def test_imsave_roundtrip(self):
|
||||
dtype = np.uint8
|
||||
np.random.seed(0)
|
||||
for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]:
|
||||
x = np.ones(shape, dtype=dtype) * np.random.rand(*shape)
|
||||
|
||||
if np.issubdtype(dtype, np.floating):
|
||||
yield self.roundtrip, x, 255
|
||||
else:
|
||||
x = (x * 255).astype(dtype)
|
||||
yield self.roundtrip, x
|
104
venv/Lib/site-packages/skimage/io/tests/test_io.py
Normal file
104
venv/Lib/site-packages/skimage/io/tests/test_io.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
import numpy as np
|
||||
from skimage import io
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_array_equal
|
||||
from skimage._shared.testing import fetch
|
||||
from skimage.data import data_dir
|
||||
|
||||
|
||||
one_by_one_jpeg = (
|
||||
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01'
|
||||
b'\x00\x01\x00\x00\xff\xdb\x00C\x00\x03\x02\x02\x02\x02'
|
||||
b'\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04'
|
||||
b'\x04\x04\x04\x08\x06\x06\x05\x06\t\x08\n\n\t\x08\t\t'
|
||||
b'\n\x0c\x0f\x0c\n\x0b\x0e\x0b\t\t\r\x11\r\x0e\x0f\x10'
|
||||
b'\x10\x11\x10\n\x0c\x12\x13\x12\x10\x13\x0f\x10\x10'
|
||||
b'\x10\xff\xc0\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11'
|
||||
b'\x00\xff\xc4\x00\x14\x00\x01\x00\x00\x00\x00\x00\x00'
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\xff\xc4\x00'
|
||||
b'\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
b'\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00'
|
||||
b'\x00?\x00*\x9f\xff\xd9'
|
||||
)
|
||||
|
||||
def test_stack_basic():
|
||||
x = np.arange(12).reshape(3, 4)
|
||||
io.push(x)
|
||||
|
||||
assert_array_equal(io.pop(), x)
|
||||
|
||||
|
||||
def test_stack_non_array():
|
||||
with testing.raises(ValueError):
|
||||
io.push([[1, 2, 3]])
|
||||
|
||||
|
||||
def test_imread_file_url():
|
||||
# tweak data path so that file URI works on both unix and windows.
|
||||
data_path = str(testing.fetch('data/camera.png'))
|
||||
data_path = data_path.replace(os.path.sep, '/')
|
||||
image_url = 'file:///{0}'.format(data_path)
|
||||
image = io.imread(image_url)
|
||||
assert image.shape == (512, 512)
|
||||
|
||||
|
||||
def test_imread_http_url(httpserver):
|
||||
# httpserver is a fixture provided by pytest-localserver
|
||||
# https://bitbucket.org/pytest-dev/pytest-localserver/
|
||||
httpserver.serve_content(one_by_one_jpeg)
|
||||
# it will serve anything you provide to it on its url.
|
||||
# we add a /test.jpg so that we can identify the content
|
||||
# by extension
|
||||
image = io.imread(httpserver.url + '/test.jpg' + '?' + 's' * 266)
|
||||
assert image.shape == (1, 1)
|
||||
|
||||
|
||||
def _named_tempfile_func(error_class):
|
||||
"""Create a mock function for NamedTemporaryFile that always raises.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
error_class : exception class
|
||||
The error that should be raised when asking for a NamedTemporaryFile.
|
||||
|
||||
Returns
|
||||
-------
|
||||
named_temp_file : callable
|
||||
A function that always raises the desired error.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Although this function has general utility for raising errors, it is
|
||||
expected to be used to raise errors that ``tempfile.NamedTemporaryFile``
|
||||
from the Python standard library could raise. As of this writing, these
|
||||
are ``FileNotFoundError``, ``FileExistsError``, ``PermissionError``, and
|
||||
``BaseException``. See
|
||||
`this comment <https://github.com/scikit-image/scikit-image/issues/3785#issuecomment-486598307>`__
|
||||
for more information.
|
||||
"""
|
||||
def named_temp_file(*args, **kwargs):
|
||||
raise error_class()
|
||||
return named_temp_file
|
||||
|
||||
|
||||
@testing.parametrize(
|
||||
'error_class', [
|
||||
FileNotFoundError, FileExistsError, PermissionError, BaseException
|
||||
]
|
||||
)
|
||||
def test_failed_temporary_file(monkeypatch, error_class):
|
||||
fetch('data/camera.png')
|
||||
# tweak data path so that file URI works on both unix and windows.
|
||||
data_path = data_dir.lstrip(os.path.sep)
|
||||
data_path = data_path.replace(os.path.sep, '/')
|
||||
image_url = 'file:///{0}/camera.png'.format(data_path)
|
||||
with monkeypatch.context():
|
||||
monkeypatch.setattr(
|
||||
tempfile, 'NamedTemporaryFile', _named_tempfile_func(error_class)
|
||||
)
|
||||
with testing.raises(error_class):
|
||||
image = io.imread(image_url)
|
135
venv/Lib/site-packages/skimage/io/tests/test_mpl_imshow.py
Normal file
135
venv/Lib/site-packages/skimage/io/tests/test_mpl_imshow.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
|
||||
import numpy as np
|
||||
from skimage import io
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def setup():
|
||||
io.reset_plugins()
|
||||
|
||||
# test images. Note that they don't have their full range for their dtype,
|
||||
# but we still expect the display range to equal the full dtype range.
|
||||
im8 = np.array([[0, 64], [128, 240]], np.uint8)
|
||||
im16 = im8.astype(np.uint16) * 256
|
||||
im64 = im8.astype(np.uint64)
|
||||
imf = im8 / 255
|
||||
im_lo = imf / 1000
|
||||
im_hi = imf + 10
|
||||
|
||||
imshow_expected_warnings = [
|
||||
r"tight_layout : falling back to Agg|\A\Z",
|
||||
r"tight_layout: falling back to Agg|\A\Z", # formatting change in mpl
|
||||
# Maptlotlib 2.2.3 seems to use np.asscalar which issues a warning
|
||||
# with numpy 1.16
|
||||
# Matplotlib 2.2.3 is the last supported version for python 2.7
|
||||
r"np.asscalar|\A\Z"
|
||||
]
|
||||
|
||||
|
||||
def n_subplots(ax_im):
|
||||
"""Return the number of subplots in the figure containing an ``AxesImage``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ax_im : matplotlib.pyplot.AxesImage object
|
||||
The input ``AxesImage``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
n : int
|
||||
The number of subplots in the corresponding figure.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function is intended to check whether a colorbar was drawn, in
|
||||
which case two subplots are expected. For standard imshows, one
|
||||
subplot is expected.
|
||||
"""
|
||||
return len(ax_im.get_figure().get_axes())
|
||||
|
||||
|
||||
def test_uint8():
|
||||
plt.figure()
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
[r"CObject type is marked|\A\Z"]):
|
||||
ax_im = io.imshow(im8)
|
||||
assert ax_im.cmap.name == 'gray'
|
||||
assert ax_im.get_clim() == (0, 255)
|
||||
assert n_subplots(ax_im) == 1
|
||||
assert ax_im.colorbar is None
|
||||
|
||||
|
||||
def test_uint16():
|
||||
plt.figure()
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
[r"CObject type is marked|\A\Z"]):
|
||||
ax_im = io.imshow(im16)
|
||||
assert ax_im.cmap.name == 'gray'
|
||||
assert ax_im.get_clim() == (0, 65535)
|
||||
assert n_subplots(ax_im) == 1
|
||||
assert ax_im.colorbar is None
|
||||
|
||||
|
||||
def test_float():
|
||||
plt.figure()
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
[r"CObject type is marked|\A\Z"]):
|
||||
ax_im = io.imshow(imf)
|
||||
assert ax_im.cmap.name == 'gray'
|
||||
assert ax_im.get_clim() == (0, 1)
|
||||
assert n_subplots(ax_im) == 1
|
||||
assert ax_im.colorbar is None
|
||||
|
||||
|
||||
def test_low_data_range():
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
["Low image data range|CObject type is marked"]):
|
||||
ax_im = io.imshow(im_lo)
|
||||
assert ax_im.get_clim() == (im_lo.min(), im_lo.max())
|
||||
# check that a colorbar was created
|
||||
assert ax_im.colorbar is not None
|
||||
|
||||
|
||||
def test_outside_standard_range():
|
||||
plt.figure()
|
||||
# Warning raised by matplotlib on Windows:
|
||||
# "The CObject type is marked Pending Deprecation in Python 2.7.
|
||||
# Please use capsule objects instead."
|
||||
# Ref: https://docs.python.org/2/c-api/cobject.html
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
["out of standard range|CObject type is marked"]):
|
||||
ax_im = io.imshow(im_hi)
|
||||
assert ax_im.get_clim() == (im_hi.min(), im_hi.max())
|
||||
assert n_subplots(ax_im) == 2
|
||||
assert ax_im.colorbar is not None
|
||||
|
||||
|
||||
def test_nonstandard_type():
|
||||
plt.figure()
|
||||
# Warning raised by matplotlib on Windows:
|
||||
# "The CObject type is marked Pending Deprecation in Python 2.7.
|
||||
# Please use capsule objects instead."
|
||||
# Ref: https://docs.python.org/2/c-api/cobject.html
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
["Low image data range|CObject type is marked"]):
|
||||
ax_im = io.imshow(im64)
|
||||
assert ax_im.get_clim() == (im64.min(), im64.max())
|
||||
assert n_subplots(ax_im) == 2
|
||||
assert ax_im.colorbar is not None
|
||||
|
||||
|
||||
def test_signed_image():
|
||||
plt.figure()
|
||||
im_signed = np.array([[-0.5, -0.2], [0.1, 0.4]])
|
||||
|
||||
with expected_warnings(imshow_expected_warnings +
|
||||
[r"CObject type is marked|\A\Z"]):
|
||||
ax_im = io.imshow(im_signed)
|
||||
assert ax_im.get_clim() == (-0.5, 0.5)
|
||||
assert n_subplots(ax_im) == 2
|
||||
assert ax_im.colorbar is not None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
np.testing.run_module_suite()
|
94
venv/Lib/site-packages/skimage/io/tests/test_multi_image.py
Normal file
94
venv/Lib/site-packages/skimage/io/tests/test_multi_image.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
import os
|
||||
|
||||
import numpy as np
|
||||
from skimage.io import use_plugin, reset_plugins
|
||||
from skimage.io.collection import MultiImage, ImageCollection
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_equal, assert_allclose
|
||||
|
||||
import pytest
|
||||
from pytest import fixture
|
||||
|
||||
@fixture
|
||||
def imgs():
|
||||
use_plugin('pil')
|
||||
|
||||
paths = [testing.fetch('data/multipage_rgb.tif'),
|
||||
testing.fetch('data/no_time_for_that_tiny.gif')]
|
||||
imgs = [MultiImage(paths[0]),
|
||||
MultiImage(paths[0], conserve_memory=False),
|
||||
MultiImage(paths[1]),
|
||||
MultiImage(paths[1], conserve_memory=False),
|
||||
ImageCollection(paths[0]),
|
||||
ImageCollection(paths[1], conserve_memory=False),
|
||||
ImageCollection(os.pathsep.join(paths))]
|
||||
yield imgs
|
||||
|
||||
reset_plugins()
|
||||
|
||||
def test_shapes(imgs):
|
||||
img = imgs[-1]
|
||||
imgs = img[:]
|
||||
assert imgs[0].shape == imgs[1].shape
|
||||
assert imgs[0].shape == (10, 10, 3)
|
||||
|
||||
def test_len(imgs):
|
||||
assert len(imgs[0]) == len(imgs[1]) == 2
|
||||
assert len(imgs[2]) == len(imgs[3]) == 24
|
||||
assert len(imgs[4]) == 2
|
||||
assert len(imgs[5]) == 24
|
||||
assert len(imgs[6]) == 26, len(imgs[6])
|
||||
|
||||
def test_slicing(imgs):
|
||||
img = imgs[-1]
|
||||
assert type(img[:]) is ImageCollection
|
||||
assert len(img[:]) == 26, len(img[:])
|
||||
assert len(img[:1]) == 1
|
||||
assert len(img[1:]) == 25
|
||||
assert_allclose(img[0], img[:1][0])
|
||||
assert_allclose(img[1], img[1:][0])
|
||||
assert_allclose(img[-1], img[::-1][0])
|
||||
assert_allclose(img[0], img[::-1][-1])
|
||||
|
||||
def test_getitem(imgs):
|
||||
for img in imgs:
|
||||
num = len(img)
|
||||
|
||||
for i in range(-num, num):
|
||||
assert type(img[i]) is np.ndarray
|
||||
assert_allclose(img[0], img[-num])
|
||||
|
||||
with testing.raises(AssertionError):
|
||||
assert_allclose(img[0], img[1])
|
||||
|
||||
with testing.raises(IndexError):
|
||||
img[num]
|
||||
with testing.raises(IndexError):
|
||||
img[-num - 1]
|
||||
|
||||
def test_files_property(imgs):
|
||||
for img in imgs:
|
||||
if isinstance(img, ImageCollection):
|
||||
continue
|
||||
|
||||
assert isinstance(img.filename, str)
|
||||
|
||||
with testing.raises(AttributeError):
|
||||
img.filename = "newfile"
|
||||
|
||||
def test_conserve_memory_property(imgs):
|
||||
for img in imgs:
|
||||
assert isinstance(img.conserve_memory, bool)
|
||||
|
||||
with testing.raises(AttributeError):
|
||||
img.conserve_memory = True
|
||||
|
||||
def test_concatenate(imgs):
|
||||
for img in imgs:
|
||||
if img[0].shape != img[-1].shape:
|
||||
with testing.raises(ValueError):
|
||||
img.concatenate()
|
||||
continue
|
||||
array = img.concatenate()
|
||||
assert_equal(array.shape, (len(img),) + img[0].shape)
|
297
venv/Lib/site-packages/skimage/io/tests/test_pil.py
Normal file
297
venv/Lib/site-packages/skimage/io/tests/test_pil.py
Normal file
|
@ -0,0 +1,297 @@
|
|||
import os
|
||||
import numpy as np
|
||||
from io import BytesIO
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from ... import img_as_float
|
||||
from .. import imread, imsave, use_plugin, reset_plugins
|
||||
|
||||
from PIL import Image
|
||||
from .._plugins.pil_plugin import (
|
||||
pil_to_ndarray, ndarray_to_pil, _palette_is_grayscale)
|
||||
from ...color import rgb2lab
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import (mono_check, color_check,
|
||||
assert_equal, assert_array_equal,
|
||||
assert_array_almost_equal,
|
||||
assert_allclose, fetch)
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
from skimage._shared._tempfile import temporary_file
|
||||
|
||||
from skimage.metrics import structural_similarity
|
||||
|
||||
|
||||
def setup():
|
||||
use_plugin('pil')
|
||||
|
||||
|
||||
def teardown():
|
||||
reset_plugins()
|
||||
|
||||
|
||||
def setup_module(self):
|
||||
"""The effect of the `plugin.use` call may be overridden by later imports.
|
||||
Call `use_plugin` directly before the tests to ensure that PIL is used.
|
||||
|
||||
"""
|
||||
try:
|
||||
use_plugin('pil')
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def test_png_round_trip():
|
||||
f = NamedTemporaryFile(suffix='.png')
|
||||
fname = f.name
|
||||
f.close()
|
||||
I = np.eye(3)
|
||||
imsave(fname, I)
|
||||
Ip = img_as_float(imread(fname))
|
||||
os.remove(fname)
|
||||
assert np.sum(np.abs(Ip-I)) < 1e-3
|
||||
|
||||
|
||||
def test_imread_as_gray():
|
||||
img = imread(fetch('data/color.png'), as_gray=True)
|
||||
assert img.ndim == 2
|
||||
assert img.dtype == np.float64
|
||||
img = imread(fetch('data/camera.png'), as_gray=True)
|
||||
# check that conversion does not happen for a gray image
|
||||
assert np.sctype2char(img.dtype) in np.typecodes['AllInteger']
|
||||
|
||||
|
||||
def test_imread_separate_channels():
|
||||
# Test that imread returns RGBA values contiguously even when they are
|
||||
# stored in separate planes.
|
||||
x = np.random.rand(3, 16, 8)
|
||||
f = NamedTemporaryFile(suffix='.tif')
|
||||
fname = f.name
|
||||
f.close()
|
||||
imsave(fname, x)
|
||||
img = imread(fname)
|
||||
os.remove(fname)
|
||||
assert img.shape == (16, 8, 3), img.shape
|
||||
|
||||
|
||||
def test_imread_multipage_rgb_tif():
|
||||
img = imread(fetch('data/multipage_rgb.tif'))
|
||||
assert img.shape == (2, 10, 10, 3), img.shape
|
||||
|
||||
|
||||
def test_imread_palette():
|
||||
img = imread(fetch('data/palette_gray.png'))
|
||||
assert img.ndim == 2
|
||||
img = imread(fetch('data/palette_color.png'))
|
||||
assert img.ndim == 3
|
||||
|
||||
|
||||
def test_imread_index_png_with_alpha():
|
||||
# The file `foo3x5x4indexed.png` was created with this array
|
||||
# (3x5 is (height)x(width)):
|
||||
dfoo = np.array([[[127, 0, 255, 255],
|
||||
[127, 0, 255, 255],
|
||||
[127, 0, 255, 255],
|
||||
[127, 0, 255, 255],
|
||||
[127, 0, 255, 255]],
|
||||
[[192, 192, 255, 0],
|
||||
[192, 192, 255, 0],
|
||||
[0, 0, 255, 0],
|
||||
[0, 0, 255, 0],
|
||||
[0, 0, 255, 0]],
|
||||
[[0, 31, 255, 255],
|
||||
[0, 31, 255, 255],
|
||||
[0, 31, 255, 255],
|
||||
[0, 31, 255, 255],
|
||||
[0, 31, 255, 255]]], dtype=np.uint8)
|
||||
img = imread(fetch('data/foo3x5x4indexed.png'))
|
||||
assert_array_equal(img, dfoo)
|
||||
|
||||
|
||||
def test_palette_is_gray():
|
||||
gray = Image.open(fetch('data/palette_gray.png'))
|
||||
assert _palette_is_grayscale(gray)
|
||||
color = Image.open(fetch('data/palette_color.png'))
|
||||
assert not _palette_is_grayscale(color)
|
||||
|
||||
|
||||
def test_bilevel():
|
||||
expected = np.zeros((10, 10))
|
||||
expected[::2] = 255
|
||||
|
||||
img = imread(fetch('data/checker_bilevel.png'))
|
||||
assert_array_equal(img, expected)
|
||||
|
||||
|
||||
def test_imread_uint16():
|
||||
expected = np.load(fetch('data/chessboard_GRAY_U8.npy'))
|
||||
img = imread(fetch('data/chessboard_GRAY_U16.tif'))
|
||||
assert np.issubdtype(img.dtype, np.uint16)
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
def test_imread_truncated_jpg():
|
||||
with testing.raises(IOError):
|
||||
imread(fetch('data/truncated.jpg'))
|
||||
|
||||
|
||||
def test_jpg_quality_arg():
|
||||
chessboard = np.load(fetch('data/chessboard_GRAY_U8.npy'))
|
||||
with temporary_file(suffix='.jpg') as jpg:
|
||||
imsave(jpg, chessboard, quality=95)
|
||||
im = imread(jpg)
|
||||
sim = structural_similarity(
|
||||
chessboard, im,
|
||||
data_range=chessboard.max() - chessboard.min())
|
||||
assert sim > 0.99
|
||||
|
||||
|
||||
def test_imread_uint16_big_endian():
|
||||
expected = np.load(fetch('data/chessboard_GRAY_U8.npy'))
|
||||
img = imread(fetch('data/chessboard_GRAY_U16B.tif'))
|
||||
assert img.dtype == np.uint16
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
class TestSave:
|
||||
def roundtrip_file(self, x):
|
||||
with temporary_file(suffix='.png') as fname:
|
||||
imsave(fname, x)
|
||||
y = imread(fname)
|
||||
return y
|
||||
|
||||
def roundtrip_pil_image(self, x):
|
||||
pil_image = ndarray_to_pil(x)
|
||||
y = pil_to_ndarray(pil_image)
|
||||
return y
|
||||
|
||||
def verify_roundtrip(self, dtype, x, y, scaling=1):
|
||||
assert_array_almost_equal((x * scaling).astype(np.int32), y)
|
||||
|
||||
def verify_imsave_roundtrip(self, roundtrip_function):
|
||||
for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]:
|
||||
for dtype in (np.uint8, np.uint16, np.float32, np.float64):
|
||||
x = np.ones(shape, dtype=dtype) * np.random.rand(*shape)
|
||||
|
||||
if np.issubdtype(dtype, np.floating):
|
||||
yield (self.verify_roundtrip, dtype, x,
|
||||
roundtrip_function(x), 255)
|
||||
else:
|
||||
x = (x * 255).astype(dtype)
|
||||
yield (self.verify_roundtrip, dtype, x,
|
||||
roundtrip_function(x))
|
||||
|
||||
def test_imsave_roundtrip_file(self):
|
||||
self.verify_imsave_roundtrip(self.roundtrip_file)
|
||||
|
||||
def test_imsave_roundtrip_pil_image(self):
|
||||
self.verify_imsave_roundtrip(self.roundtrip_pil_image)
|
||||
|
||||
|
||||
def test_imsave_incorrect_dimension():
|
||||
with temporary_file(suffix='.png') as fname:
|
||||
with testing.raises(ValueError):
|
||||
with expected_warnings([fname + ' is a low contrast image']):
|
||||
imsave(fname, np.zeros((2, 3, 3, 1)))
|
||||
with testing.raises(ValueError):
|
||||
with expected_warnings([fname + ' is a low contrast image']):
|
||||
imsave(fname, np.zeros((2, 3, 2)))
|
||||
# test that low contrast check is ignored
|
||||
with testing.raises(ValueError):
|
||||
with expected_warnings([]):
|
||||
imsave(fname, np.zeros((2, 3, 2)), check_contrast=False)
|
||||
|
||||
|
||||
def test_imsave_filelike():
|
||||
shape = (2, 2)
|
||||
image = np.zeros(shape)
|
||||
s = BytesIO()
|
||||
|
||||
# save to file-like object
|
||||
with expected_warnings(['is a low contrast image']):
|
||||
imsave(s, image)
|
||||
|
||||
# read from file-like object
|
||||
s.seek(0)
|
||||
out = imread(s)
|
||||
assert_equal(out.shape, shape)
|
||||
assert_allclose(out, image)
|
||||
|
||||
|
||||
def test_imsave_boolean_input():
|
||||
shape = (2, 2)
|
||||
image = np.eye(*shape, dtype=np.bool)
|
||||
s = BytesIO()
|
||||
|
||||
# save to file-like object
|
||||
with expected_warnings(
|
||||
['is a boolean image: setting True to 255 and False to 0']):
|
||||
imsave(s, image)
|
||||
|
||||
# read from file-like object
|
||||
s.seek(0)
|
||||
out = imread(s)
|
||||
assert_equal(out.shape, shape)
|
||||
assert_allclose(out.astype(bool), image)
|
||||
|
||||
|
||||
def test_imexport_imimport():
|
||||
shape = (2, 2)
|
||||
image = np.zeros(shape)
|
||||
pil_image = ndarray_to_pil(image)
|
||||
out = pil_to_ndarray(pil_image)
|
||||
assert_equal(out.shape, shape)
|
||||
|
||||
|
||||
def test_all_color():
|
||||
with expected_warnings(['.* is a boolean image']):
|
||||
color_check('pil')
|
||||
with expected_warnings(['.* is a boolean image']):
|
||||
color_check('pil', 'bmp')
|
||||
|
||||
|
||||
def test_all_mono():
|
||||
with expected_warnings(['.* is a boolean image']):
|
||||
mono_check('pil')
|
||||
|
||||
|
||||
def test_multi_page_gif():
|
||||
img = imread(fetch('data/no_time_for_that_tiny.gif'))
|
||||
assert img.shape == (24, 25, 14, 3), img.shape
|
||||
img2 = imread(fetch('data/no_time_for_that_tiny.gif'),
|
||||
img_num=5)
|
||||
assert img2.shape == (25, 14, 3)
|
||||
assert_allclose(img[5], img2)
|
||||
|
||||
|
||||
def test_cmyk():
|
||||
ref = imread(fetch('data/color.png'))
|
||||
|
||||
img = Image.open(fetch('data/color.png'))
|
||||
img = img.convert('CMYK')
|
||||
|
||||
f = NamedTemporaryFile(suffix='.jpg')
|
||||
fname = f.name
|
||||
f.close()
|
||||
img.save(fname)
|
||||
try:
|
||||
img.close()
|
||||
except AttributeError: # `close` not available on PIL
|
||||
pass
|
||||
|
||||
new = imread(fname)
|
||||
|
||||
ref_lab = rgb2lab(ref)
|
||||
new_lab = rgb2lab(new)
|
||||
|
||||
for i in range(3):
|
||||
newi = np.ascontiguousarray(new_lab[:, :, i])
|
||||
refi = np.ascontiguousarray(ref_lab[:, :, i])
|
||||
sim = structural_similarity(refi, newi,
|
||||
data_range=refi.max() - refi.min())
|
||||
assert sim > 0.99
|
||||
|
||||
|
||||
def test_extreme_palette():
|
||||
img = imread(fetch('data/green_palette.png'))
|
||||
assert_equal(img.ndim, 3)
|
72
venv/Lib/site-packages/skimage/io/tests/test_plugin.py
Normal file
72
venv/Lib/site-packages/skimage/io/tests/test_plugin.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
from contextlib import contextmanager
|
||||
|
||||
from skimage import io
|
||||
from skimage.io import manage_plugins
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_equal
|
||||
|
||||
|
||||
|
||||
priority_plugin = 'pil'
|
||||
|
||||
|
||||
def setup():
|
||||
io.use_plugin('pil')
|
||||
|
||||
|
||||
def teardown_module():
|
||||
io.reset_plugins()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def protect_preferred_plugins():
|
||||
"""Contexts where `preferred_plugins` can be modified w/o side-effects."""
|
||||
preferred_plugins = manage_plugins.preferred_plugins.copy()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
manage_plugins.preferred_plugins = preferred_plugins
|
||||
|
||||
|
||||
def test_failed_use():
|
||||
with testing.raises(ValueError):
|
||||
manage_plugins.use_plugin('asd')
|
||||
|
||||
|
||||
def test_use_priority():
|
||||
manage_plugins.use_plugin(priority_plugin)
|
||||
plug, func = manage_plugins.plugin_store['imread'][0]
|
||||
assert_equal(plug, priority_plugin)
|
||||
|
||||
manage_plugins.use_plugin('matplotlib')
|
||||
plug, func = manage_plugins.plugin_store['imread'][0]
|
||||
assert_equal(plug, 'matplotlib')
|
||||
|
||||
|
||||
def test_load_preferred_plugins_all():
|
||||
from skimage.io._plugins import pil_plugin, matplotlib_plugin
|
||||
|
||||
with protect_preferred_plugins():
|
||||
manage_plugins.preferred_plugins = {'all': ['pil'],
|
||||
'imshow': ['matplotlib']}
|
||||
manage_plugins.reset_plugins()
|
||||
|
||||
for plugin_type in ('imread', 'imsave'):
|
||||
plug, func = manage_plugins.plugin_store[plugin_type][0]
|
||||
assert func == getattr(pil_plugin, plugin_type)
|
||||
plug, func = manage_plugins.plugin_store['imshow'][0]
|
||||
assert func == getattr(matplotlib_plugin, 'imshow')
|
||||
|
||||
|
||||
def test_load_preferred_plugins_imread():
|
||||
from skimage.io._plugins import pil_plugin, matplotlib_plugin
|
||||
|
||||
with protect_preferred_plugins():
|
||||
manage_plugins.preferred_plugins['imread'] = ['pil']
|
||||
manage_plugins.reset_plugins()
|
||||
|
||||
plug, func = manage_plugins.plugin_store['imread'][0]
|
||||
assert func == pil_plugin.imread
|
||||
plug, func = manage_plugins.plugin_store['imshow'][0]
|
||||
assert func == matplotlib_plugin.imshow, func.__module__
|
69
venv/Lib/site-packages/skimage/io/tests/test_plugin_util.py
Normal file
69
venv/Lib/site-packages/skimage/io/tests/test_plugin_util.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
import numpy as np
|
||||
from skimage.io._plugins.util import prepare_for_display, WindowManager
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_array_equal, TestCase
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
|
||||
np.random.seed(0)
|
||||
|
||||
|
||||
class TestPrepareForDisplay(TestCase):
|
||||
def test_basic(self):
|
||||
prepare_for_display(np.random.rand(10, 10))
|
||||
|
||||
def test_dtype(self):
|
||||
x = prepare_for_display(np.random.rand(10, 15))
|
||||
assert x.dtype == np.dtype(np.uint8)
|
||||
|
||||
def test_grey(self):
|
||||
tmp = np.arange(12, dtype=float).reshape((4, 3)) / 11
|
||||
x = prepare_for_display(tmp)
|
||||
assert_array_equal(x[..., 0], x[..., 2])
|
||||
assert x[0, 0, 0] == 0
|
||||
assert x[3, 2, 0] == 255
|
||||
|
||||
def test_color(self):
|
||||
prepare_for_display(np.random.rand(10, 10, 3))
|
||||
|
||||
def test_alpha(self):
|
||||
prepare_for_display(np.random.rand(10, 10, 4))
|
||||
|
||||
def test_wrong_dimensionality(self):
|
||||
with testing.raises(ValueError):
|
||||
prepare_for_display(np.random.rand(10, 10, 1, 1))
|
||||
|
||||
def test_wrong_depth(self):
|
||||
with testing.raises(ValueError):
|
||||
prepare_for_display(np.random.rand(10, 10, 5))
|
||||
|
||||
|
||||
class TestWindowManager(TestCase):
|
||||
callback_called = False
|
||||
|
||||
@testing.fixture(autouse=True)
|
||||
def setup(self):
|
||||
self.wm = WindowManager()
|
||||
self.wm.acquire('test')
|
||||
|
||||
def test_add_window(self):
|
||||
self.wm.add_window('window1')
|
||||
self.wm.remove_window('window1')
|
||||
|
||||
def callback(self):
|
||||
self.callback_called = True
|
||||
|
||||
def test_callback(self):
|
||||
self.wm.register_callback(self.callback)
|
||||
self.wm.add_window('window')
|
||||
self.wm.remove_window('window')
|
||||
assert self.callback_called
|
||||
|
||||
def test_has_images(self):
|
||||
assert not self.wm.has_windows()
|
||||
self.wm.add_window('window')
|
||||
assert self.wm.has_windows()
|
||||
|
||||
def teardown(self):
|
||||
self.wm._release('test')
|
68
venv/Lib/site-packages/skimage/io/tests/test_sift.py
Normal file
68
venv/Lib/site-packages/skimage/io/tests/test_sift.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from skimage.io import load_sift, load_surf
|
||||
|
||||
from skimage._shared.testing import assert_equal
|
||||
|
||||
|
||||
def test_load_sift():
|
||||
f = NamedTemporaryFile(delete=False)
|
||||
fname = f.name
|
||||
f.close()
|
||||
f = open(fname, 'wb')
|
||||
f.write(b'''2 128
|
||||
133.92 135.88 14.38 -2.732
|
||||
3 12 23 38 10 15 78 20 39 67 42 8 12 8 39 35 118 43 17 0
|
||||
0 1 12 109 9 2 6 0 0 21 46 22 14 18 51 19 5 9 41 52
|
||||
65 30 3 21 55 49 26 30 118 118 25 12 8 3 2 60 53 56 72 20
|
||||
7 10 16 7 88 23 13 15 12 11 11 71 45 7 4 49 82 38 38 91
|
||||
118 15 2 16 33 3 5 118 98 38 6 19 36 1 0 15 64 22 1 2
|
||||
6 11 18 61 31 3 0 6 15 23 118 118 13 0 0 35 38 18 40 96
|
||||
24 1 0 13 17 3 24 98
|
||||
132.36 99.75 11.45 -2.910
|
||||
94 32 7 2 13 7 5 23 121 94 13 5 0 0 4 59 13 30 71 32
|
||||
0 6 32 11 25 32 13 0 0 16 51 5 44 50 0 3 33 55 11 9
|
||||
121 121 12 9 6 3 0 18 55 60 48 44 44 9 0 2 106 117 13 2
|
||||
1 0 1 1 37 1 1 25 80 35 15 41 121 3 0 2 14 3 2 121
|
||||
51 11 0 20 93 6 0 20 109 57 3 4 5 0 0 28 21 2 0 5
|
||||
13 12 75 119 35 0 0 13 28 14 37 121 12 0 0 21 46 5 11 93
|
||||
29 0 0 3 14 4 11 99''')
|
||||
f.close()
|
||||
|
||||
# Check whether loading by filename works
|
||||
load_sift(fname)
|
||||
|
||||
with open(fname, 'r') as f:
|
||||
features = load_sift(f)
|
||||
os.remove(fname)
|
||||
|
||||
assert_equal(len(features), 2)
|
||||
assert_equal(len(features['data'][0]), 128)
|
||||
assert_equal(features['row'][0], 133.92)
|
||||
assert_equal(features['column'][1], 99.75)
|
||||
|
||||
|
||||
def test_load_surf():
|
||||
f = NamedTemporaryFile(delete=False)
|
||||
fname = f.name
|
||||
f.close()
|
||||
f = open(fname, 'wb')
|
||||
f.write(b'''65
|
||||
2
|
||||
38.3727 62.0491 0.0371343 0 0.0371343 -1 -0.0705589 0.0130983 -0.00460534 0.132168 -0.0718833 0.0320583 -0.0134032 0.0988654 -0.0542241 0.0171002 -0.00135754 0.105755 -0.0362088 0.0151748 -0.00694272 0.0610017 -0.247091 0.109605 -0.0337623 0.0813307 -0.24185 0.278548 -0.0494523 0.107804 -0.166312 0.0691584 -0.0288199 0.138476 -0.110956 0.0280772 -0.0752509 0.0736344 -0.22667 0.164226 -0.0544717 0.0388139 -0.30194 0.33065 -0.0537507 0.0596398 -0.245395 0.110925 -0.0603322 0.0239389 -0.18726 0.0374145 -0.0355872 0.0140762 -0.129022 0.135104 -0.0703396 0.0374049 -0.24256 0.222544 -0.0536354 0.0501252 -0.209004 0.0971316 -0.0550094 0.0229767 -0.125547 0.0317879 -0.0291574 0.0124569
|
||||
68.5773 61.474 0.0313267 0 0.0313267 1 -0.10198 0.130987 -0.0321845 0.0487543 -0.0900435 0.121113 -0.100917 0.0444702 -0.0151742 0.107604 -0.0542035 0.014069 -0.00594097 0.0339933 -0.00994295 0.0127262 -0.125613 0.192551 -0.0174399 0.0433488 -0.272698 0.164641 -0.0676735 0.0467444 -0.0527907 0.258005 -0.0818114 0.0440569 -0.0104433 0.0548934 -0.0323454 0.0145296 -0.112357 0.199223 -0.0532903 0.0332622 -0.342481 0.207469 -0.0526129 0.0741355 -0.256234 0.402708 -0.108296 0.117362 -0.0560274 0.128856 -0.123509 0.0510046 -0.0198793 0.0775934 -0.103863 0.00406679 -0.10264 0.1312 -0.108244 0.0812913 -0.127868 0.182924 -0.0680942 0.071913 -0.0858004 0.144806 -0.0176522 0.0686146''')
|
||||
f.close()
|
||||
|
||||
# Check whether loading by filename works
|
||||
load_surf(fname)
|
||||
|
||||
f = open(fname, 'r')
|
||||
features = load_surf(f)
|
||||
f.close()
|
||||
os.remove(fname)
|
||||
|
||||
assert_equal(len(features), 2)
|
||||
assert_equal(len(features['data'][0]), 64)
|
||||
assert_equal(features['column'][1], 68.5773)
|
||||
assert_equal(features['row'][0], 62.0491)
|
87
venv/Lib/site-packages/skimage/io/tests/test_simpleitk.py
Normal file
87
venv/Lib/site-packages/skimage/io/tests/test_simpleitk.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
import os.path
|
||||
import numpy as np
|
||||
import unittest
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from skimage import data
|
||||
from skimage.io import imread, imsave, use_plugin, reset_plugins
|
||||
from skimage._shared import testing
|
||||
|
||||
from pytest import importorskip
|
||||
|
||||
importorskip('SimpleITK')
|
||||
|
||||
np.random.seed(0)
|
||||
|
||||
def teardown():
|
||||
reset_plugins()
|
||||
|
||||
|
||||
def setup_module(self):
|
||||
"""The effect of the `plugin.use` call may be overridden by later imports.
|
||||
Call `use_plugin` directly before the tests to ensure that SimpleITK is
|
||||
used.
|
||||
|
||||
"""
|
||||
use_plugin('simpleitk')
|
||||
|
||||
|
||||
def test_imread_as_gray():
|
||||
img = imread(testing.fetch('data/color.png'), as_gray=True)
|
||||
assert img.ndim == 2
|
||||
assert img.dtype == np.float64
|
||||
img = imread(testing.fetch('data/camera.png'), as_gray=True)
|
||||
# check that conversion does not happen for a gray image
|
||||
assert np.sctype2char(img.dtype) in np.typecodes['AllInteger']
|
||||
|
||||
|
||||
def test_bilevel():
|
||||
expected = np.zeros((10, 10))
|
||||
expected[::2] = 255
|
||||
|
||||
img = imread(testing.fetch('data/checker_bilevel.png'))
|
||||
np.testing.assert_array_equal(img, expected)
|
||||
|
||||
"""
|
||||
#TODO: This test causes a Segmentation fault
|
||||
def test_imread_truncated_jpg():
|
||||
assert_raises((RuntimeError, ValueError),
|
||||
imread,
|
||||
testing.fetch('data/truncated.jpg'))
|
||||
"""
|
||||
|
||||
|
||||
def test_imread_uint16():
|
||||
expected = np.load(testing.fetch('data/chessboard_GRAY_U8.npy'))
|
||||
img = imread(testing.fetch('data/chessboard_GRAY_U16.tif'))
|
||||
assert np.issubdtype(img.dtype, np.uint16)
|
||||
np.testing.assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
def test_imread_uint16_big_endian():
|
||||
expected = np.load(testing.fetch('data/chessboard_GRAY_U8.npy'))
|
||||
img = imread(testing.fetch('data/chessboard_GRAY_U16B.tif'))
|
||||
np.testing.assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
class TestSave(unittest.TestCase):
|
||||
def roundtrip(self, dtype, x):
|
||||
f = NamedTemporaryFile(suffix='.mha')
|
||||
fname = f.name
|
||||
f.close()
|
||||
imsave(fname, x)
|
||||
y = imread(fname)
|
||||
|
||||
np.testing.assert_array_almost_equal(x, y)
|
||||
|
||||
def test_imsave_roundtrip(self):
|
||||
for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]:
|
||||
for dtype in (np.uint8, np.uint16, np.float32, np.float64):
|
||||
x = np.ones(shape, dtype=dtype) * np.random.rand(*shape)
|
||||
|
||||
if np.issubdtype(dtype, np.floating):
|
||||
yield self.roundtrip, dtype, x
|
||||
else:
|
||||
x = (x * 255).astype(dtype)
|
||||
yield self.roundtrip, dtype, x
|
75
venv/Lib/site-packages/skimage/io/tests/test_tifffile.py
Normal file
75
venv/Lib/site-packages/skimage/io/tests/test_tifffile.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import itertools
|
||||
from tempfile import NamedTemporaryFile
|
||||
from skimage.io import imread, imsave, use_plugin, reset_plugins
|
||||
import numpy as np
|
||||
|
||||
from skimage._shared.testing import (assert_array_equal,
|
||||
assert_array_almost_equal,
|
||||
parametrize, fetch)
|
||||
from skimage._shared import testing
|
||||
|
||||
|
||||
def setup():
|
||||
use_plugin('tifffile')
|
||||
np.random.seed(0)
|
||||
|
||||
|
||||
def teardown():
|
||||
reset_plugins()
|
||||
|
||||
|
||||
def test_imread_uint16():
|
||||
expected = np.load(fetch('data/chessboard_GRAY_U8.npy'))
|
||||
img = imread(fetch('data/chessboard_GRAY_U16.tif'))
|
||||
assert img.dtype == np.uint16
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
def test_imread_uint16_big_endian():
|
||||
expected = np.load(fetch('data/chessboard_GRAY_U8.npy'))
|
||||
img = imread(fetch('data/chessboard_GRAY_U16B.tif'))
|
||||
assert img.dtype == np.uint16
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
def test_imread_multipage_rgb_tif():
|
||||
img = imread(fetch('data/multipage_rgb.tif'))
|
||||
assert img.shape == (2, 10, 10, 3), img.shape
|
||||
|
||||
|
||||
def test_tifffile_kwarg_passthrough ():
|
||||
img = imread(fetch('data/multipage.tif'), key=[1],
|
||||
multifile=False, multifile_close=True, fastij=True,
|
||||
is_ome=True)
|
||||
assert img.shape == (15, 10), img.shape
|
||||
|
||||
|
||||
def test_imread_handle():
|
||||
expected = np.load(fetch('data/chessboard_GRAY_U8.npy'))
|
||||
with open(fetch('data/chessboard_GRAY_U16.tif'), 'rb') as fh:
|
||||
img = imread(fh)
|
||||
assert img.dtype == np.uint16
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
class TestSave:
|
||||
def roundtrip(self, dtype, x):
|
||||
f = NamedTemporaryFile(suffix='.tif')
|
||||
fname = f.name
|
||||
f.close()
|
||||
imsave(fname, x, check_contrast=False)
|
||||
y = imread(fname)
|
||||
assert_array_equal(x, y)
|
||||
|
||||
shapes = ((10, 10), (10, 10, 3), (10, 10, 4))
|
||||
dtypes = (np.uint8, np.uint16, np.float32, np.int16, np.float64)
|
||||
|
||||
@parametrize("shape, dtype", itertools.product(shapes, dtypes))
|
||||
def test_imsave_roundtrip(self, shape, dtype):
|
||||
x = np.random.rand(*shape)
|
||||
|
||||
if not np.issubdtype(dtype, np.floating):
|
||||
x = (x * np.iinfo(dtype).max).astype(dtype)
|
||||
else:
|
||||
x = x.astype(dtype)
|
||||
self.roundtrip(dtype, x)
|
43
venv/Lib/site-packages/skimage/io/util.py
Normal file
43
venv/Lib/site-packages/skimage/io/util.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import urllib.parse
|
||||
import urllib.request
|
||||
from urllib.error import URLError, HTTPError
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
URL_REGEX = re.compile(r'http://|https://|ftp://|file://|file:\\')
|
||||
|
||||
|
||||
def is_url(filename):
|
||||
"""Return True if string is an http or ftp path."""
|
||||
return (isinstance(filename, str) and
|
||||
URL_REGEX.match(filename) is not None)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def file_or_url_context(resource_name):
|
||||
"""Yield name of file from the given resource (i.e. file or url)."""
|
||||
if is_url(resource_name):
|
||||
url_components = urllib.parse.urlparse(resource_name)
|
||||
_, ext = os.path.splitext(url_components.path)
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as f:
|
||||
u = urllib.request.urlopen(resource_name)
|
||||
f.write(u.read())
|
||||
# f must be closed before yielding
|
||||
yield f.name
|
||||
except (URLError, HTTPError):
|
||||
# could not open URL
|
||||
os.remove(f.name)
|
||||
raise
|
||||
except (FileNotFoundError, FileExistsError,
|
||||
PermissionError, BaseException):
|
||||
# could not create temporary file
|
||||
raise
|
||||
else:
|
||||
os.remove(f.name)
|
||||
else:
|
||||
yield resource_name
|
Loading…
Add table
Add a link
Reference in a new issue