Spaces:
Paused
Paused
Upload 879 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- skimage/__init__.py +182 -0
- skimage/__init__.pyi +41 -0
- skimage/__pycache__/__init__.cpython-310.pyc +0 -0
- skimage/__pycache__/conftest.cpython-310.pyc +0 -0
- skimage/_shared/__init__.py +0 -0
- skimage/_shared/__pycache__/__init__.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/_dependency_checks.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/_geometry.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/_tempfile.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/_warnings.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/compat.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/coord.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/dtype.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/filters.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/tester.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/testing.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/utils.cpython-310.pyc +0 -0
- skimage/_shared/__pycache__/version_requirements.cpython-310.pyc +0 -0
- skimage/_shared/_dependency_checks.py +3 -0
- skimage/_shared/_geometry.py +54 -0
- skimage/_shared/_tempfile.py +28 -0
- skimage/_shared/_warnings.py +149 -0
- skimage/_shared/compat.py +30 -0
- skimage/_shared/coord.py +125 -0
- skimage/_shared/dtype.py +73 -0
- skimage/_shared/fast_exp.cp310-win_amd64.lib +0 -0
- skimage/_shared/fast_exp.cp310-win_amd64.pyd +0 -0
- skimage/_shared/fast_exp.h +47 -0
- skimage/_shared/filters.py +142 -0
- skimage/_shared/geometry.cp310-win_amd64.lib +0 -0
- skimage/_shared/geometry.cp310-win_amd64.pyd +0 -0
- skimage/_shared/interpolation.cp310-win_amd64.lib +0 -0
- skimage/_shared/interpolation.cp310-win_amd64.pyd +0 -0
- skimage/_shared/tester.py +131 -0
- skimage/_shared/testing.py +291 -0
- skimage/_shared/tests/__init__.py +0 -0
- skimage/_shared/tests/__pycache__/__init__.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_coord.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_dtype.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_fast_exp.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_geometry.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_interpolation.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_safe_as_int.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_testing.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_utils.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_version_requirements.cpython-310.pyc +0 -0
- skimage/_shared/tests/__pycache__/test_warnings.cpython-310.pyc +0 -0
- skimage/_shared/tests/test_coord.py +91 -0
- skimage/_shared/tests/test_dtype.py +14 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
color_features.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
image_subset.json filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
color_features.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
image_subset.json filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
skimage/filters/rank/generic_cy.cp310-win_amd64.pyd filter=lfs diff=lfs merge=lfs -text
|
skimage/__init__.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Image Processing for Python
|
| 2 |
+
|
| 3 |
+
``scikit-image`` (a.k.a. ``skimage``) is a collection of algorithms for image
|
| 4 |
+
processing and computer vision.
|
| 5 |
+
|
| 6 |
+
The main package of ``skimage`` only provides a few utilities for converting
|
| 7 |
+
between image data types; for most features, you need to import one of the
|
| 8 |
+
following subpackages:
|
| 9 |
+
|
| 10 |
+
Subpackages
|
| 11 |
+
-----------
|
| 12 |
+
color
|
| 13 |
+
Color space conversion.
|
| 14 |
+
data
|
| 15 |
+
Test images and example data.
|
| 16 |
+
draw
|
| 17 |
+
Drawing primitives (lines, text, etc.) that operate on NumPy arrays.
|
| 18 |
+
exposure
|
| 19 |
+
Image intensity adjustment, e.g., histogram equalization, etc.
|
| 20 |
+
feature
|
| 21 |
+
Feature detection and extraction, e.g., texture analysis corners, etc.
|
| 22 |
+
filters
|
| 23 |
+
Sharpening, edge finding, rank filters, thresholding, etc.
|
| 24 |
+
graph
|
| 25 |
+
Graph-theoretic operations, e.g., shortest paths.
|
| 26 |
+
io
|
| 27 |
+
Reading, saving, and displaying images and video.
|
| 28 |
+
measure
|
| 29 |
+
Measurement of image properties, e.g., region properties and contours.
|
| 30 |
+
metrics
|
| 31 |
+
Metrics corresponding to images, e.g. distance metrics, similarity, etc.
|
| 32 |
+
morphology
|
| 33 |
+
Morphological operations, e.g., opening or skeletonization.
|
| 34 |
+
restoration
|
| 35 |
+
Restoration algorithms, e.g., deconvolution algorithms, denoising, etc.
|
| 36 |
+
segmentation
|
| 37 |
+
Partitioning an image into multiple regions.
|
| 38 |
+
transform
|
| 39 |
+
Geometric and other transforms, e.g., rotation or the Radon transform.
|
| 40 |
+
util
|
| 41 |
+
Generic utilities.
|
| 42 |
+
|
| 43 |
+
Utility Functions
|
| 44 |
+
-----------------
|
| 45 |
+
img_as_float
|
| 46 |
+
Convert an image to floating point format, with values in [0, 1].
|
| 47 |
+
Is similar to `img_as_float64`, but will not convert lower-precision
|
| 48 |
+
floating point arrays to `float64`.
|
| 49 |
+
img_as_float32
|
| 50 |
+
Convert an image to single-precision (32-bit) floating point format,
|
| 51 |
+
with values in [0, 1].
|
| 52 |
+
img_as_float64
|
| 53 |
+
Convert an image to double-precision (64-bit) floating point format,
|
| 54 |
+
with values in [0, 1].
|
| 55 |
+
img_as_uint
|
| 56 |
+
Convert an image to unsigned integer format, with values in [0, 65535].
|
| 57 |
+
img_as_int
|
| 58 |
+
Convert an image to signed integer format, with values in [-32768, 32767].
|
| 59 |
+
img_as_ubyte
|
| 60 |
+
Convert an image to unsigned byte format, with values in [0, 255].
|
| 61 |
+
img_as_bool
|
| 62 |
+
Convert an image to boolean format, with values either True or False.
|
| 63 |
+
dtype_limits
|
| 64 |
+
Return intensity limits, i.e. (min, max) tuple, of the image's dtype.
|
| 65 |
+
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
__version__ = '0.23.2'
|
| 69 |
+
|
| 70 |
+
import lazy_loader as lazy
|
| 71 |
+
|
| 72 |
+
__getattr__, __lazy_dir__, _ = lazy.attach_stub(__name__, __file__)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def __dir__():
|
| 76 |
+
return __lazy_dir__() + ['__version__']
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# Logic for checking for improper install and importing while in the source
|
| 80 |
+
# tree when package has not been installed inplace.
|
| 81 |
+
# Code adapted from scikit-learn's __check_build module.
|
| 82 |
+
_INPLACE_MSG = """
|
| 83 |
+
It appears that you are importing a local scikit-image source tree. For
|
| 84 |
+
this, you need to have an inplace install. Maybe you are in the source
|
| 85 |
+
directory and you need to try from another location."""
|
| 86 |
+
|
| 87 |
+
_STANDARD_MSG = """
|
| 88 |
+
Your install of scikit-image appears to be broken.
|
| 89 |
+
Try re-installing the package following the instructions at:
|
| 90 |
+
https://scikit-image.org/docs/stable/user_guide/install.html"""
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _raise_build_error(e):
|
| 94 |
+
# Raise a comprehensible error
|
| 95 |
+
import os.path as osp
|
| 96 |
+
|
| 97 |
+
local_dir = osp.split(__file__)[0]
|
| 98 |
+
msg = _STANDARD_MSG
|
| 99 |
+
if local_dir == "skimage":
|
| 100 |
+
# Picking up the local install: this will work only if the
|
| 101 |
+
# install is an 'inplace build'
|
| 102 |
+
msg = _INPLACE_MSG
|
| 103 |
+
raise ImportError(
|
| 104 |
+
f"{e}\nIt seems that scikit-image has not been built correctly.\n{msg}"
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
try:
|
| 109 |
+
# This variable is injected in the __builtins__ by the build
|
| 110 |
+
# process. It used to enable importing subpackages of skimage when
|
| 111 |
+
# the binaries are not built
|
| 112 |
+
__SKIMAGE_SETUP__
|
| 113 |
+
except NameError:
|
| 114 |
+
__SKIMAGE_SETUP__ = False
|
| 115 |
+
|
| 116 |
+
if __SKIMAGE_SETUP__:
|
| 117 |
+
import sys
|
| 118 |
+
|
| 119 |
+
sys.stderr.write('Partial import of skimage during the build process.\n')
|
| 120 |
+
# We are not importing the rest of the scikit during the build
|
| 121 |
+
# process, as it may not be compiled yet
|
| 122 |
+
else:
|
| 123 |
+
try:
|
| 124 |
+
from ._shared import geometry
|
| 125 |
+
|
| 126 |
+
del geometry
|
| 127 |
+
except ImportError as e:
|
| 128 |
+
_raise_build_error(e)
|
| 129 |
+
|
| 130 |
+
# Legacy imports into the root namespace; not advertised in __all__
|
| 131 |
+
from .util.dtype import (
|
| 132 |
+
dtype_limits,
|
| 133 |
+
img_as_float32,
|
| 134 |
+
img_as_float64,
|
| 135 |
+
img_as_float,
|
| 136 |
+
img_as_int,
|
| 137 |
+
img_as_uint,
|
| 138 |
+
img_as_ubyte,
|
| 139 |
+
img_as_bool,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
from .util.lookfor import lookfor
|
| 143 |
+
|
| 144 |
+
from .data import data_dir
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
if 'dev' in __version__:
|
| 148 |
+
# Append last commit date and hash to dev version information, if available
|
| 149 |
+
|
| 150 |
+
import subprocess
|
| 151 |
+
import os.path
|
| 152 |
+
|
| 153 |
+
try:
|
| 154 |
+
p = subprocess.Popen(
|
| 155 |
+
['git', 'log', '-1', '--format="%h %aI"'],
|
| 156 |
+
stdout=subprocess.PIPE,
|
| 157 |
+
stderr=subprocess.PIPE,
|
| 158 |
+
cwd=os.path.dirname(__file__),
|
| 159 |
+
)
|
| 160 |
+
except FileNotFoundError:
|
| 161 |
+
pass
|
| 162 |
+
else:
|
| 163 |
+
out, err = p.communicate()
|
| 164 |
+
if p.returncode == 0:
|
| 165 |
+
git_hash, git_date = (
|
| 166 |
+
out.decode('utf-8')
|
| 167 |
+
.strip()
|
| 168 |
+
.replace('"', '')
|
| 169 |
+
.split('T')[0]
|
| 170 |
+
.replace('-', '')
|
| 171 |
+
.split()
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
__version__ = '+'.join(
|
| 175 |
+
[tag for tag in __version__.split('+') if not tag.startswith('git')]
|
| 176 |
+
)
|
| 177 |
+
__version__ += f'+git{git_date}.{git_hash}'
|
| 178 |
+
|
| 179 |
+
from skimage._shared.tester import PytestTester # noqa
|
| 180 |
+
|
| 181 |
+
test = PytestTester(__name__)
|
| 182 |
+
del PytestTester
|
skimage/__init__.pyi
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
submodules = [
|
| 2 |
+
'color',
|
| 3 |
+
'data',
|
| 4 |
+
'draw',
|
| 5 |
+
'exposure',
|
| 6 |
+
'feature',
|
| 7 |
+
'filters',
|
| 8 |
+
'future',
|
| 9 |
+
'graph',
|
| 10 |
+
'io',
|
| 11 |
+
'measure',
|
| 12 |
+
'metrics',
|
| 13 |
+
'morphology',
|
| 14 |
+
'registration',
|
| 15 |
+
'restoration',
|
| 16 |
+
'segmentation',
|
| 17 |
+
'transform',
|
| 18 |
+
'util',
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
__all__ = submodules + ['__version__'] # noqa: F822
|
| 22 |
+
|
| 23 |
+
from . import (
|
| 24 |
+
color,
|
| 25 |
+
data,
|
| 26 |
+
draw,
|
| 27 |
+
exposure,
|
| 28 |
+
feature,
|
| 29 |
+
filters,
|
| 30 |
+
future,
|
| 31 |
+
graph,
|
| 32 |
+
io,
|
| 33 |
+
measure,
|
| 34 |
+
metrics,
|
| 35 |
+
morphology,
|
| 36 |
+
registration,
|
| 37 |
+
restoration,
|
| 38 |
+
segmentation,
|
| 39 |
+
transform,
|
| 40 |
+
util,
|
| 41 |
+
)
|
skimage/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (4.7 kB). View file
|
|
|
skimage/__pycache__/conftest.cpython-310.pyc
ADDED
|
Binary file (453 Bytes). View file
|
|
|
skimage/_shared/__init__.py
ADDED
|
File without changes
|
skimage/_shared/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (153 Bytes). View file
|
|
|
skimage/_shared/__pycache__/_dependency_checks.cpython-310.pyc
ADDED
|
Binary file (260 Bytes). View file
|
|
|
skimage/_shared/__pycache__/_geometry.cpython-310.pyc
ADDED
|
Binary file (1.64 kB). View file
|
|
|
skimage/_shared/__pycache__/_tempfile.cpython-310.pyc
ADDED
|
Binary file (995 Bytes). View file
|
|
|
skimage/_shared/__pycache__/_warnings.cpython-310.pyc
ADDED
|
Binary file (4.2 kB). View file
|
|
|
skimage/_shared/__pycache__/compat.cpython-310.pyc
ADDED
|
Binary file (519 Bytes). View file
|
|
|
skimage/_shared/__pycache__/coord.cpython-310.pyc
ADDED
|
Binary file (3.83 kB). View file
|
|
|
skimage/_shared/__pycache__/dtype.cpython-310.pyc
ADDED
|
Binary file (2.06 kB). View file
|
|
|
skimage/_shared/__pycache__/filters.cpython-310.pyc
ADDED
|
Binary file (4.98 kB). View file
|
|
|
skimage/_shared/__pycache__/tester.cpython-310.pyc
ADDED
|
Binary file (3.43 kB). View file
|
|
|
skimage/_shared/__pycache__/testing.cpython-310.pyc
ADDED
|
Binary file (8.3 kB). View file
|
|
|
skimage/_shared/__pycache__/utils.cpython-310.pyc
ADDED
|
Binary file (25.6 kB). View file
|
|
|
skimage/_shared/__pycache__/version_requirements.cpython-310.pyc
ADDED
|
Binary file (4.12 kB). View file
|
|
|
skimage/_shared/_dependency_checks.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .version_requirements import is_installed
|
| 2 |
+
|
| 3 |
+
has_mpl = is_installed("matplotlib", ">=3.3")
|
skimage/_shared/_geometry.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = ['polygon_clip', 'polygon_area']
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
from .version_requirements import require
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@require("matplotlib", ">=3.3")
|
| 9 |
+
def polygon_clip(rp, cp, r0, c0, r1, c1):
|
| 10 |
+
"""Clip a polygon to the given bounding box.
|
| 11 |
+
|
| 12 |
+
Parameters
|
| 13 |
+
----------
|
| 14 |
+
rp, cp : (K,) ndarray of double
|
| 15 |
+
Row and column coordinates of the polygon.
|
| 16 |
+
(r0, c0), (r1, c1) : double
|
| 17 |
+
Top-left and bottom-right coordinates of the bounding box.
|
| 18 |
+
|
| 19 |
+
Returns
|
| 20 |
+
-------
|
| 21 |
+
r_clipped, c_clipped : (L,) ndarray of double
|
| 22 |
+
Coordinates of clipped polygon.
|
| 23 |
+
|
| 24 |
+
Notes
|
| 25 |
+
-----
|
| 26 |
+
This makes use of Sutherland-Hodgman clipping as implemented in
|
| 27 |
+
AGG 2.4 and exposed in Matplotlib.
|
| 28 |
+
|
| 29 |
+
"""
|
| 30 |
+
from matplotlib import path, transforms
|
| 31 |
+
|
| 32 |
+
poly = path.Path(np.vstack((rp, cp)).T, closed=True)
|
| 33 |
+
clip_rect = transforms.Bbox([[r0, c0], [r1, c1]])
|
| 34 |
+
poly_clipped = poly.clip_to_bbox(clip_rect).to_polygons()[0]
|
| 35 |
+
|
| 36 |
+
return poly_clipped[:, 0], poly_clipped[:, 1]
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def polygon_area(pr, pc):
|
| 40 |
+
"""Compute the area of a polygon.
|
| 41 |
+
|
| 42 |
+
Parameters
|
| 43 |
+
----------
|
| 44 |
+
pr, pc : (K,) array of float
|
| 45 |
+
Polygon row and column coordinates.
|
| 46 |
+
|
| 47 |
+
Returns
|
| 48 |
+
-------
|
| 49 |
+
a : float
|
| 50 |
+
Area of the polygon.
|
| 51 |
+
"""
|
| 52 |
+
pr = np.asarray(pr)
|
| 53 |
+
pc = np.asarray(pc)
|
| 54 |
+
return 0.5 * np.abs(np.sum((pc[:-1] * pr[1:]) - (pc[1:] * pr[:-1])))
|
skimage/_shared/_tempfile.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from tempfile import NamedTemporaryFile
|
| 2 |
+
from contextlib import contextmanager
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@contextmanager
|
| 7 |
+
def temporary_file(suffix=''):
|
| 8 |
+
"""Yield a writeable temporary filename that is deleted on context exit.
|
| 9 |
+
|
| 10 |
+
Parameters
|
| 11 |
+
----------
|
| 12 |
+
suffix : string, optional
|
| 13 |
+
The suffix for the file.
|
| 14 |
+
|
| 15 |
+
Examples
|
| 16 |
+
--------
|
| 17 |
+
>>> import numpy as np
|
| 18 |
+
>>> from skimage import io
|
| 19 |
+
>>> with temporary_file('.tif') as tempfile:
|
| 20 |
+
... im = np.arange(25, dtype=np.uint8).reshape((5, 5))
|
| 21 |
+
... io.imsave(tempfile, im)
|
| 22 |
+
... assert np.all(io.imread(tempfile) == im)
|
| 23 |
+
"""
|
| 24 |
+
with NamedTemporaryFile(suffix=suffix, delete=False) as tempfile_stream:
|
| 25 |
+
tempfile = tempfile_stream.name
|
| 26 |
+
|
| 27 |
+
yield tempfile
|
| 28 |
+
os.remove(tempfile)
|
skimage/_shared/_warnings.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from contextlib import contextmanager
|
| 2 |
+
import sys
|
| 3 |
+
import warnings
|
| 4 |
+
import re
|
| 5 |
+
import functools
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
__all__ = ['all_warnings', 'expected_warnings', 'warn']
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# A version of `warnings.warn` with a default stacklevel of 2.
|
| 12 |
+
# functool is used so as not to increase the call stack accidentally
|
| 13 |
+
warn = functools.partial(warnings.warn, stacklevel=2)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@contextmanager
|
| 17 |
+
def all_warnings():
|
| 18 |
+
"""
|
| 19 |
+
Context for use in testing to ensure that all warnings are raised.
|
| 20 |
+
|
| 21 |
+
Examples
|
| 22 |
+
--------
|
| 23 |
+
>>> import warnings
|
| 24 |
+
>>> def foo():
|
| 25 |
+
... warnings.warn(RuntimeWarning("bar"), stacklevel=2)
|
| 26 |
+
|
| 27 |
+
We raise the warning once, while the warning filter is set to "once".
|
| 28 |
+
Hereafter, the warning is invisible, even with custom filters:
|
| 29 |
+
|
| 30 |
+
>>> with warnings.catch_warnings():
|
| 31 |
+
... warnings.simplefilter('once')
|
| 32 |
+
... foo() # doctest: +SKIP
|
| 33 |
+
|
| 34 |
+
We can now run ``foo()`` without a warning being raised:
|
| 35 |
+
|
| 36 |
+
>>> from numpy.testing import assert_warns
|
| 37 |
+
>>> foo() # doctest: +SKIP
|
| 38 |
+
|
| 39 |
+
To catch the warning, we call in the help of ``all_warnings``:
|
| 40 |
+
|
| 41 |
+
>>> with all_warnings():
|
| 42 |
+
... assert_warns(RuntimeWarning, foo)
|
| 43 |
+
"""
|
| 44 |
+
# _warnings.py is on the critical import path.
|
| 45 |
+
# Since this is a testing only function, we lazy import inspect.
|
| 46 |
+
import inspect
|
| 47 |
+
|
| 48 |
+
# Whenever a warning is triggered, Python adds a __warningregistry__
|
| 49 |
+
# member to the *calling* module. The exercise here is to find
|
| 50 |
+
# and eradicate all those breadcrumbs that were left lying around.
|
| 51 |
+
#
|
| 52 |
+
# We proceed by first searching all parent calling frames and explicitly
|
| 53 |
+
# clearing their warning registries (necessary for the doctests above to
|
| 54 |
+
# pass). Then, we search for all submodules of skimage and clear theirs
|
| 55 |
+
# as well (necessary for the skimage test suite to pass).
|
| 56 |
+
|
| 57 |
+
frame = inspect.currentframe()
|
| 58 |
+
if frame:
|
| 59 |
+
for f in inspect.getouterframes(frame):
|
| 60 |
+
f[0].f_locals['__warningregistry__'] = {}
|
| 61 |
+
del frame
|
| 62 |
+
|
| 63 |
+
for mod_name, mod in list(sys.modules.items()):
|
| 64 |
+
try:
|
| 65 |
+
mod.__warningregistry__.clear()
|
| 66 |
+
except AttributeError:
|
| 67 |
+
pass
|
| 68 |
+
|
| 69 |
+
with warnings.catch_warnings(record=True) as w:
|
| 70 |
+
warnings.simplefilter("always")
|
| 71 |
+
yield w
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
@contextmanager
|
| 75 |
+
def expected_warnings(matching):
|
| 76 |
+
r"""Context for use in testing to catch known warnings matching regexes
|
| 77 |
+
|
| 78 |
+
Parameters
|
| 79 |
+
----------
|
| 80 |
+
matching : None or a list of strings or compiled regexes
|
| 81 |
+
Regexes for the desired warning to catch
|
| 82 |
+
If matching is None, this behaves as a no-op.
|
| 83 |
+
|
| 84 |
+
Examples
|
| 85 |
+
--------
|
| 86 |
+
>>> import numpy as np
|
| 87 |
+
>>> rng = np.random.default_rng()
|
| 88 |
+
>>> image = rng.integers(0, 2**16, size=(100, 100), dtype=np.uint16)
|
| 89 |
+
>>> # rank filters are slow when bit-depth exceeds 10 bits
|
| 90 |
+
>>> from skimage import filters
|
| 91 |
+
>>> with expected_warnings(['Bad rank filter performance']):
|
| 92 |
+
... median_filtered = filters.rank.median(image)
|
| 93 |
+
|
| 94 |
+
Notes
|
| 95 |
+
-----
|
| 96 |
+
Uses `all_warnings` to ensure all warnings are raised.
|
| 97 |
+
Upon exiting, it checks the recorded warnings for the desired matching
|
| 98 |
+
pattern(s).
|
| 99 |
+
Raises a ValueError if any match was not found or an unexpected
|
| 100 |
+
warning was raised.
|
| 101 |
+
Allows for three types of behaviors: `and`, `or`, and `optional` matches.
|
| 102 |
+
This is done to accommodate different build environments or loop conditions
|
| 103 |
+
that may produce different warnings. The behaviors can be combined.
|
| 104 |
+
If you pass multiple patterns, you get an orderless `and`, where all of the
|
| 105 |
+
warnings must be raised.
|
| 106 |
+
If you use the `|` operator in a pattern, you can catch one of several
|
| 107 |
+
warnings.
|
| 108 |
+
Finally, you can use `|\A\Z` in a pattern to signify it as optional.
|
| 109 |
+
|
| 110 |
+
"""
|
| 111 |
+
if isinstance(matching, str):
|
| 112 |
+
raise ValueError(
|
| 113 |
+
'``matching`` should be a list of strings and not ' 'a string itself.'
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
# Special case for disabling the context manager
|
| 117 |
+
if matching is None:
|
| 118 |
+
yield None
|
| 119 |
+
return
|
| 120 |
+
|
| 121 |
+
strict_warnings = os.environ.get('SKIMAGE_TEST_STRICT_WARNINGS', '1')
|
| 122 |
+
if strict_warnings.lower() == 'true':
|
| 123 |
+
strict_warnings = True
|
| 124 |
+
elif strict_warnings.lower() == 'false':
|
| 125 |
+
strict_warnings = False
|
| 126 |
+
else:
|
| 127 |
+
strict_warnings = bool(int(strict_warnings))
|
| 128 |
+
|
| 129 |
+
with all_warnings() as w:
|
| 130 |
+
# enter context
|
| 131 |
+
yield w
|
| 132 |
+
# exited user context, check the recorded warnings
|
| 133 |
+
# Allow users to provide None
|
| 134 |
+
while None in matching:
|
| 135 |
+
matching.remove(None)
|
| 136 |
+
remaining = [m for m in matching if r'\A\Z' not in m.split('|')]
|
| 137 |
+
for warn in w:
|
| 138 |
+
found = False
|
| 139 |
+
for match in matching:
|
| 140 |
+
if re.search(match, str(warn.message)) is not None:
|
| 141 |
+
found = True
|
| 142 |
+
if match in remaining:
|
| 143 |
+
remaining.remove(match)
|
| 144 |
+
if strict_warnings and not found:
|
| 145 |
+
raise ValueError(f'Unexpected warning: {str(warn.message)}')
|
| 146 |
+
if strict_warnings and (len(remaining) > 0):
|
| 147 |
+
newline = "\n"
|
| 148 |
+
msg = f"No warning raised matching:{newline}{newline.join(remaining)}"
|
| 149 |
+
raise ValueError(msg)
|
skimage/_shared/compat.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility helpers for dependencies."""
|
| 2 |
+
|
| 3 |
+
from packaging.version import parse
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import scipy as sp
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
"NP_COPY_IF_NEEDED",
|
| 11 |
+
"SCIPY_CG_TOL_PARAM_NAME",
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
NUMPY_LT_2_0_0 = parse(np.__version__) < parse('2.0.0.dev0')
|
| 16 |
+
|
| 17 |
+
# With NumPy 2.0.0, `copy=False` now raises a ValueError if the copy cannot be
|
| 18 |
+
# made. The previous behavior to only copy if needed is provided with `copy=None`.
|
| 19 |
+
# During the transition period, use this symbol instead.
|
| 20 |
+
# Remove once NumPy 2.0.0 is the minimal required version.
|
| 21 |
+
# https://numpy.org/devdocs/release/2.0.0-notes.html#new-copy-keyword-meaning-for-array-and-asarray-constructors
|
| 22 |
+
# https://github.com/numpy/numpy/pull/25168
|
| 23 |
+
NP_COPY_IF_NEEDED = False if NUMPY_LT_2_0_0 else None
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
SCIPY_LT_1_12 = parse(sp.__version__) < parse('1.12')
|
| 27 |
+
|
| 28 |
+
# Starting in SciPy v1.12, 'scipy.sparse.linalg.cg' keyword argument `tol` is
|
| 29 |
+
# deprecated in favor of `rtol`.
|
| 30 |
+
SCIPY_CG_TOL_PARAM_NAME = "tol" if SCIPY_LT_1_12 else "rtol"
|
skimage/_shared/coord.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from scipy.spatial import cKDTree, distance
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def _ensure_spacing(coord, spacing, p_norm, max_out):
|
| 6 |
+
"""Returns a subset of coord where a minimum spacing is guaranteed.
|
| 7 |
+
|
| 8 |
+
Parameters
|
| 9 |
+
----------
|
| 10 |
+
coord : ndarray
|
| 11 |
+
The coordinates of the considered points.
|
| 12 |
+
spacing : float
|
| 13 |
+
the maximum allowed spacing between the points.
|
| 14 |
+
p_norm : float
|
| 15 |
+
Which Minkowski p-norm to use. Should be in the range [1, inf].
|
| 16 |
+
A finite large p may cause a ValueError if overflow can occur.
|
| 17 |
+
``inf`` corresponds to the Chebyshev distance and 2 to the
|
| 18 |
+
Euclidean distance.
|
| 19 |
+
max_out: int
|
| 20 |
+
If not None, at most the first ``max_out`` candidates are
|
| 21 |
+
returned.
|
| 22 |
+
|
| 23 |
+
Returns
|
| 24 |
+
-------
|
| 25 |
+
output : ndarray
|
| 26 |
+
A subset of coord where a minimum spacing is guaranteed.
|
| 27 |
+
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
# Use KDtree to find the peaks that are too close to each other
|
| 31 |
+
tree = cKDTree(coord)
|
| 32 |
+
|
| 33 |
+
indices = tree.query_ball_point(coord, r=spacing, p=p_norm)
|
| 34 |
+
rejected_peaks_indices = set()
|
| 35 |
+
naccepted = 0
|
| 36 |
+
for idx, candidates in enumerate(indices):
|
| 37 |
+
if idx not in rejected_peaks_indices:
|
| 38 |
+
# keep current point and the points at exactly spacing from it
|
| 39 |
+
candidates.remove(idx)
|
| 40 |
+
dist = distance.cdist(
|
| 41 |
+
[coord[idx]], coord[candidates], distance.minkowski, p=p_norm
|
| 42 |
+
).reshape(-1)
|
| 43 |
+
candidates = [c for c, d in zip(candidates, dist) if d < spacing]
|
| 44 |
+
|
| 45 |
+
# candidates.remove(keep)
|
| 46 |
+
rejected_peaks_indices.update(candidates)
|
| 47 |
+
naccepted += 1
|
| 48 |
+
if max_out is not None and naccepted >= max_out:
|
| 49 |
+
break
|
| 50 |
+
|
| 51 |
+
# Remove the peaks that are too close to each other
|
| 52 |
+
output = np.delete(coord, tuple(rejected_peaks_indices), axis=0)
|
| 53 |
+
if max_out is not None:
|
| 54 |
+
output = output[:max_out]
|
| 55 |
+
|
| 56 |
+
return output
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def ensure_spacing(
|
| 60 |
+
coords,
|
| 61 |
+
spacing=1,
|
| 62 |
+
p_norm=np.inf,
|
| 63 |
+
min_split_size=50,
|
| 64 |
+
max_out=None,
|
| 65 |
+
*,
|
| 66 |
+
max_split_size=2000,
|
| 67 |
+
):
|
| 68 |
+
"""Returns a subset of coord where a minimum spacing is guaranteed.
|
| 69 |
+
|
| 70 |
+
Parameters
|
| 71 |
+
----------
|
| 72 |
+
coords : array_like
|
| 73 |
+
The coordinates of the considered points.
|
| 74 |
+
spacing : float
|
| 75 |
+
the maximum allowed spacing between the points.
|
| 76 |
+
p_norm : float
|
| 77 |
+
Which Minkowski p-norm to use. Should be in the range [1, inf].
|
| 78 |
+
A finite large p may cause a ValueError if overflow can occur.
|
| 79 |
+
``inf`` corresponds to the Chebyshev distance and 2 to the
|
| 80 |
+
Euclidean distance.
|
| 81 |
+
min_split_size : int
|
| 82 |
+
Minimum split size used to process ``coords`` by batch to save
|
| 83 |
+
memory. If None, the memory saving strategy is not applied.
|
| 84 |
+
max_out : int
|
| 85 |
+
If not None, only the first ``max_out`` candidates are returned.
|
| 86 |
+
max_split_size : int
|
| 87 |
+
Maximum split size used to process ``coords`` by batch to save
|
| 88 |
+
memory. This number was decided by profiling with a large number
|
| 89 |
+
of points. Too small a number results in too much looping in
|
| 90 |
+
Python instead of C, slowing down the process, while too large
|
| 91 |
+
a number results in large memory allocations, slowdowns, and,
|
| 92 |
+
potentially, in the process being killed -- see gh-6010. See
|
| 93 |
+
benchmark results `here
|
| 94 |
+
<https://github.com/scikit-image/scikit-image/pull/6035#discussion_r751518691>`_.
|
| 95 |
+
|
| 96 |
+
Returns
|
| 97 |
+
-------
|
| 98 |
+
output : array_like
|
| 99 |
+
A subset of coord where a minimum spacing is guaranteed.
|
| 100 |
+
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
output = coords
|
| 104 |
+
if len(coords):
|
| 105 |
+
coords = np.atleast_2d(coords)
|
| 106 |
+
if min_split_size is None:
|
| 107 |
+
batch_list = [coords]
|
| 108 |
+
else:
|
| 109 |
+
coord_count = len(coords)
|
| 110 |
+
split_idx = [min_split_size]
|
| 111 |
+
split_size = min_split_size
|
| 112 |
+
while coord_count - split_idx[-1] > max_split_size:
|
| 113 |
+
split_size *= 2
|
| 114 |
+
split_idx.append(split_idx[-1] + min(split_size, max_split_size))
|
| 115 |
+
batch_list = np.array_split(coords, split_idx)
|
| 116 |
+
|
| 117 |
+
output = np.zeros((0, coords.shape[1]), dtype=coords.dtype)
|
| 118 |
+
for batch in batch_list:
|
| 119 |
+
output = _ensure_spacing(
|
| 120 |
+
np.vstack([output, batch]), spacing, p_norm, max_out
|
| 121 |
+
)
|
| 122 |
+
if max_out is not None and len(output) >= max_out:
|
| 123 |
+
break
|
| 124 |
+
|
| 125 |
+
return output
|
skimage/_shared/dtype.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
# Define classes of supported dtypes and Python scalar types
|
| 4 |
+
# Variables ending in `_dtypes` only contain numpy.dtypes of the respective
|
| 5 |
+
# class; variables ending in `_types` additionally include Python scalar types.
|
| 6 |
+
signed_integer_dtypes = {np.int8, np.int16, np.int32, np.int64}
|
| 7 |
+
signed_integer_types = signed_integer_dtypes | {int}
|
| 8 |
+
|
| 9 |
+
unsigned_integer_dtypes = {np.uint8, np.uint16, np.uint32, np.uint64}
|
| 10 |
+
|
| 11 |
+
integer_dtypes = signed_integer_dtypes | unsigned_integer_dtypes
|
| 12 |
+
integer_types = signed_integer_types | unsigned_integer_dtypes
|
| 13 |
+
|
| 14 |
+
floating_dtypes = {np.float16, np.float32, np.float64}
|
| 15 |
+
floating_types = floating_dtypes | {float}
|
| 16 |
+
|
| 17 |
+
complex_dtypes = {np.complex64, np.complex128}
|
| 18 |
+
complex_types = complex_dtypes | {complex}
|
| 19 |
+
|
| 20 |
+
inexact_dtypes = floating_dtypes | complex_dtypes
|
| 21 |
+
inexact_types = floating_types | complex_types
|
| 22 |
+
|
| 23 |
+
bool_types = {np.dtype(bool), bool}
|
| 24 |
+
|
| 25 |
+
numeric_dtypes = integer_dtypes | inexact_dtypes | {np.bool_}
|
| 26 |
+
numeric_types = integer_types | inexact_types | bool_types
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def numeric_dtype_min_max(dtype):
|
| 30 |
+
"""Return minimum and maximum representable value for a given dtype.
|
| 31 |
+
|
| 32 |
+
A convenient wrapper around `numpy.finfo` and `numpy.iinfo` that
|
| 33 |
+
additionally supports numpy.bool as well.
|
| 34 |
+
|
| 35 |
+
Parameters
|
| 36 |
+
----------
|
| 37 |
+
dtype : numpy.dtype
|
| 38 |
+
The dtype. Tries to convert Python "types" such as int or float, to
|
| 39 |
+
the corresponding NumPy dtype.
|
| 40 |
+
|
| 41 |
+
Returns
|
| 42 |
+
-------
|
| 43 |
+
min, max : number
|
| 44 |
+
Minimum and maximum of the given `dtype`. These scalars are themselves
|
| 45 |
+
of the given `dtype`.
|
| 46 |
+
|
| 47 |
+
Examples
|
| 48 |
+
--------
|
| 49 |
+
>>> import numpy as np
|
| 50 |
+
>>> numeric_dtype_min_max(np.uint8)
|
| 51 |
+
(0, 255)
|
| 52 |
+
>>> numeric_dtype_min_max(bool)
|
| 53 |
+
(False, True)
|
| 54 |
+
>>> numeric_dtype_min_max(np.float64)
|
| 55 |
+
(-1.7976931348623157e+308, 1.7976931348623157e+308)
|
| 56 |
+
>>> numeric_dtype_min_max(int)
|
| 57 |
+
(-9223372036854775808, 9223372036854775807)
|
| 58 |
+
"""
|
| 59 |
+
dtype = np.dtype(dtype)
|
| 60 |
+
if np.issubdtype(dtype, np.integer):
|
| 61 |
+
info = np.iinfo(dtype)
|
| 62 |
+
min_ = dtype.type(info.min)
|
| 63 |
+
max_ = dtype.type(info.max)
|
| 64 |
+
elif np.issubdtype(dtype, np.inexact):
|
| 65 |
+
info = np.finfo(dtype)
|
| 66 |
+
min_ = info.min
|
| 67 |
+
max_ = info.max
|
| 68 |
+
elif np.issubdtype(dtype, np.dtype(bool)):
|
| 69 |
+
min_ = dtype.type(False)
|
| 70 |
+
max_ = dtype.type(True)
|
| 71 |
+
else:
|
| 72 |
+
raise ValueError(f"unsupported dtype {dtype!r}")
|
| 73 |
+
return min_, max_
|
skimage/_shared/fast_exp.cp310-win_amd64.lib
ADDED
|
Binary file (1.37 kB). View file
|
|
|
skimage/_shared/fast_exp.cp310-win_amd64.pyd
ADDED
|
Binary file (53.8 kB). View file
|
|
|
skimage/_shared/fast_exp.h
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* A fast approximation of the exponential function.
|
| 2 |
+
* Reference [1]: https://schraudolph.org/pubs/Schraudolph99.pdf
|
| 3 |
+
* Reference [2]: https://doi.org/10.1162/089976600300015033
|
| 4 |
+
* Additional improvements by Leonid Bloch. */
|
| 5 |
+
|
| 6 |
+
#include <stdint.h>
|
| 7 |
+
|
| 8 |
+
/* use just EXP_A = 1512775 for integer version, to avoid FP calculations */
|
| 9 |
+
#define EXP_A (1512775.3951951856938) /* 2^20/ln2 */
|
| 10 |
+
/* For min. RMS error */
|
| 11 |
+
#define EXP_BC 1072632447 /* 1023*2^20 - 60801 */
|
| 12 |
+
/* For min. max. relative error */
|
| 13 |
+
/* #define EXP_BC 1072647449 */ /* 1023*2^20 - 45799 */
|
| 14 |
+
/* For min. mean relative error */
|
| 15 |
+
/* #define EXP_BC 1072625005 */ /* 1023*2^20 - 68243 */
|
| 16 |
+
|
| 17 |
+
__inline double _fast_exp (double y)
|
| 18 |
+
{
|
| 19 |
+
union
|
| 20 |
+
{
|
| 21 |
+
double d;
|
| 22 |
+
struct { int32_t i, j; } n;
|
| 23 |
+
char t[8];
|
| 24 |
+
} _eco;
|
| 25 |
+
|
| 26 |
+
_eco.n.i = 1;
|
| 27 |
+
|
| 28 |
+
switch(_eco.t[0]) {
|
| 29 |
+
case 1:
|
| 30 |
+
/* Little endian */
|
| 31 |
+
_eco.n.j = (int32_t)(EXP_A*(y)) + EXP_BC;
|
| 32 |
+
_eco.n.i = 0;
|
| 33 |
+
break;
|
| 34 |
+
case 0:
|
| 35 |
+
/* Big endian */
|
| 36 |
+
_eco.n.i = (int32_t)(EXP_A*(y)) + EXP_BC;
|
| 37 |
+
_eco.n.j = 0;
|
| 38 |
+
break;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
return _eco.d;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
__inline float _fast_expf (float y)
|
| 45 |
+
{
|
| 46 |
+
return (float)_fast_exp((double)y);
|
| 47 |
+
}
|
skimage/_shared/filters.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Filters used across multiple skimage submodules.
|
| 2 |
+
|
| 3 |
+
These are defined here to avoid circular imports.
|
| 4 |
+
|
| 5 |
+
The unit tests remain under skimage/filters/tests/
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from collections.abc import Iterable
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
from scipy import ndimage as ndi
|
| 12 |
+
|
| 13 |
+
from .._shared.utils import (
|
| 14 |
+
_supported_float_type,
|
| 15 |
+
convert_to_float,
|
| 16 |
+
deprecate_parameter,
|
| 17 |
+
DEPRECATED,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@deprecate_parameter(
|
| 22 |
+
"output", new_name="out", start_version="0.23", stop_version="0.25"
|
| 23 |
+
)
|
| 24 |
+
def gaussian(
|
| 25 |
+
image,
|
| 26 |
+
sigma=1,
|
| 27 |
+
output=DEPRECATED,
|
| 28 |
+
mode='nearest',
|
| 29 |
+
cval=0,
|
| 30 |
+
preserve_range=False,
|
| 31 |
+
truncate=4.0,
|
| 32 |
+
*,
|
| 33 |
+
channel_axis=None,
|
| 34 |
+
out=None,
|
| 35 |
+
):
|
| 36 |
+
"""Multi-dimensional Gaussian filter.
|
| 37 |
+
|
| 38 |
+
Parameters
|
| 39 |
+
----------
|
| 40 |
+
image : ndarray
|
| 41 |
+
Input image (grayscale or color) to filter.
|
| 42 |
+
sigma : scalar or sequence of scalars, optional
|
| 43 |
+
Standard deviation for Gaussian kernel. The standard
|
| 44 |
+
deviations of the Gaussian filter are given for each axis as a
|
| 45 |
+
sequence, or as a single number, in which case it is equal for
|
| 46 |
+
all axes.
|
| 47 |
+
mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
|
| 48 |
+
The ``mode`` parameter determines how the array borders are
|
| 49 |
+
handled, where ``cval`` is the value when mode is equal to
|
| 50 |
+
'constant'. Default is 'nearest'.
|
| 51 |
+
cval : scalar, optional
|
| 52 |
+
Value to fill past edges of input if ``mode`` is 'constant'. Default
|
| 53 |
+
is 0.0
|
| 54 |
+
preserve_range : bool, optional
|
| 55 |
+
If True, keep the original range of values. Otherwise, the input
|
| 56 |
+
``image`` is converted according to the conventions of ``img_as_float``
|
| 57 |
+
(Normalized first to values [-1.0 ; 1.0] or [0 ; 1.0] depending on
|
| 58 |
+
dtype of input)
|
| 59 |
+
|
| 60 |
+
For more information, see:
|
| 61 |
+
https://scikit-image.org/docs/dev/user_guide/data_types.html
|
| 62 |
+
truncate : float, optional
|
| 63 |
+
Truncate the filter at this many standard deviations.
|
| 64 |
+
channel_axis : int or None, optional
|
| 65 |
+
If None, the image is assumed to be a grayscale (single channel) image.
|
| 66 |
+
Otherwise, this parameter indicates which axis of the array corresponds
|
| 67 |
+
to channels.
|
| 68 |
+
|
| 69 |
+
.. versionadded:: 0.19
|
| 70 |
+
`channel_axis` was added in 0.19.
|
| 71 |
+
out : ndarray, optional
|
| 72 |
+
If given, the filtered image will be stored in this array.
|
| 73 |
+
|
| 74 |
+
.. versionadded:: 0.23
|
| 75 |
+
`out` was added in 0.23.
|
| 76 |
+
|
| 77 |
+
Returns
|
| 78 |
+
-------
|
| 79 |
+
filtered_image : ndarray
|
| 80 |
+
the filtered array
|
| 81 |
+
|
| 82 |
+
Notes
|
| 83 |
+
-----
|
| 84 |
+
This function is a wrapper around :func:`scipy.ndimage.gaussian_filter`.
|
| 85 |
+
|
| 86 |
+
Integer arrays are converted to float.
|
| 87 |
+
|
| 88 |
+
`out` should be of floating-point data type since `gaussian` converts the
|
| 89 |
+
input `image` to float. If `out` is not provided, another array
|
| 90 |
+
will be allocated and returned as the result.
|
| 91 |
+
|
| 92 |
+
The multi-dimensional filter is implemented as a sequence of
|
| 93 |
+
one-dimensional convolution filters. The intermediate arrays are
|
| 94 |
+
stored in the same data type as the output. Therefore, for output
|
| 95 |
+
types with a limited precision, the results may be imprecise
|
| 96 |
+
because intermediate results may be stored with insufficient
|
| 97 |
+
precision.
|
| 98 |
+
|
| 99 |
+
Examples
|
| 100 |
+
--------
|
| 101 |
+
>>> import skimage as ski
|
| 102 |
+
>>> a = np.zeros((3, 3))
|
| 103 |
+
>>> a[1, 1] = 1
|
| 104 |
+
>>> a
|
| 105 |
+
array([[0., 0., 0.],
|
| 106 |
+
[0., 1., 0.],
|
| 107 |
+
[0., 0., 0.]])
|
| 108 |
+
>>> ski.filters.gaussian(a, sigma=0.4) # mild smoothing
|
| 109 |
+
array([[0.00163116, 0.03712502, 0.00163116],
|
| 110 |
+
[0.03712502, 0.84496158, 0.03712502],
|
| 111 |
+
[0.00163116, 0.03712502, 0.00163116]])
|
| 112 |
+
>>> ski.filters.gaussian(a, sigma=1) # more smoothing
|
| 113 |
+
array([[0.05855018, 0.09653293, 0.05855018],
|
| 114 |
+
[0.09653293, 0.15915589, 0.09653293],
|
| 115 |
+
[0.05855018, 0.09653293, 0.05855018]])
|
| 116 |
+
>>> # Several modes are possible for handling boundaries
|
| 117 |
+
>>> ski.filters.gaussian(a, sigma=1, mode='reflect')
|
| 118 |
+
array([[0.08767308, 0.12075024, 0.08767308],
|
| 119 |
+
[0.12075024, 0.16630671, 0.12075024],
|
| 120 |
+
[0.08767308, 0.12075024, 0.08767308]])
|
| 121 |
+
>>> # For RGB images, each is filtered separately
|
| 122 |
+
>>> image = ski.data.astronaut()
|
| 123 |
+
>>> filtered_img = ski.filters.gaussian(image, sigma=1, channel_axis=-1)
|
| 124 |
+
|
| 125 |
+
"""
|
| 126 |
+
if np.any(np.asarray(sigma) < 0.0):
|
| 127 |
+
raise ValueError("Sigma values less than zero are not valid")
|
| 128 |
+
if channel_axis is not None:
|
| 129 |
+
# do not filter across channels
|
| 130 |
+
if not isinstance(sigma, Iterable):
|
| 131 |
+
sigma = [sigma] * (image.ndim - 1)
|
| 132 |
+
if len(sigma) == image.ndim - 1:
|
| 133 |
+
sigma = list(sigma)
|
| 134 |
+
sigma.insert(channel_axis % image.ndim, 0)
|
| 135 |
+
image = convert_to_float(image, preserve_range)
|
| 136 |
+
float_dtype = _supported_float_type(image.dtype)
|
| 137 |
+
image = image.astype(float_dtype, copy=False)
|
| 138 |
+
if (out is not None) and (not np.issubdtype(out.dtype, np.floating)):
|
| 139 |
+
raise ValueError(f"dtype of `out` must be float; got {out.dtype!r}.")
|
| 140 |
+
return ndi.gaussian_filter(
|
| 141 |
+
image, sigma, output=out, mode=mode, cval=cval, truncate=truncate
|
| 142 |
+
)
|
skimage/_shared/geometry.cp310-win_amd64.lib
ADDED
|
Binary file (1.37 kB). View file
|
|
|
skimage/_shared/geometry.cp310-win_amd64.pyd
ADDED
|
Binary file (118 kB). View file
|
|
|
skimage/_shared/interpolation.cp310-win_amd64.lib
ADDED
|
Binary file (1.42 kB). View file
|
|
|
skimage/_shared/interpolation.cp310-win_amd64.pyd
ADDED
|
Binary file (36.9 kB). View file
|
|
|
skimage/_shared/tester.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def _show_skimage_info():
|
| 6 |
+
import skimage
|
| 7 |
+
|
| 8 |
+
print(f"skimage version {skimage.__version__}")
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class PytestTester:
|
| 12 |
+
"""
|
| 13 |
+
Pytest test runner.
|
| 14 |
+
|
| 15 |
+
This class is made available in ``skimage._shared.testing``, and a test
|
| 16 |
+
function is typically added to a package's __init__.py like so::
|
| 17 |
+
|
| 18 |
+
from skimage._shared.testing import PytestTester
|
| 19 |
+
test = PytestTester(__name__)
|
| 20 |
+
del PytestTester
|
| 21 |
+
|
| 22 |
+
Calling this test function finds and runs all tests associated with the
|
| 23 |
+
module and all its sub-modules.
|
| 24 |
+
|
| 25 |
+
Attributes
|
| 26 |
+
----------
|
| 27 |
+
module_name : str
|
| 28 |
+
Full path to the package to test.
|
| 29 |
+
|
| 30 |
+
Parameters
|
| 31 |
+
----------
|
| 32 |
+
module_name : module name
|
| 33 |
+
The name of the module to test.
|
| 34 |
+
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __init__(self, module_name):
|
| 38 |
+
self.module_name = module_name
|
| 39 |
+
|
| 40 |
+
def __call__(
|
| 41 |
+
self,
|
| 42 |
+
label='fast',
|
| 43 |
+
verbose=1,
|
| 44 |
+
extra_argv=None,
|
| 45 |
+
doctests=False,
|
| 46 |
+
coverage=False,
|
| 47 |
+
durations=-1,
|
| 48 |
+
tests=None,
|
| 49 |
+
):
|
| 50 |
+
"""
|
| 51 |
+
Run tests for module using pytest.
|
| 52 |
+
|
| 53 |
+
Parameters
|
| 54 |
+
----------
|
| 55 |
+
label : {'fast', 'full'}, optional
|
| 56 |
+
Identifies the tests to run. When set to 'fast', tests decorated
|
| 57 |
+
with `pytest.mark.slow` are skipped, when 'full', the slow marker
|
| 58 |
+
is ignored.
|
| 59 |
+
verbose : int, optional
|
| 60 |
+
Verbosity value for test outputs, in the range 1-3. Default is 1.
|
| 61 |
+
extra_argv : list, optional
|
| 62 |
+
List with any extra arguments to pass to pytests.
|
| 63 |
+
doctests : bool, optional
|
| 64 |
+
.. note:: Not supported
|
| 65 |
+
coverage : bool, optional
|
| 66 |
+
If True, report coverage of scikit-image code. Default is False.
|
| 67 |
+
Requires installation of (pip) pytest-cov.
|
| 68 |
+
durations : int, optional
|
| 69 |
+
If < 0, do nothing, If 0, report time of all tests, if > 0,
|
| 70 |
+
report the time of the slowest `timer` tests. Default is -1.
|
| 71 |
+
tests : test or list of tests
|
| 72 |
+
Tests to be executed with pytest '--pyargs'
|
| 73 |
+
|
| 74 |
+
Returns
|
| 75 |
+
-------
|
| 76 |
+
result : bool
|
| 77 |
+
Return True on success, false otherwise.
|
| 78 |
+
"""
|
| 79 |
+
import pytest
|
| 80 |
+
|
| 81 |
+
module = sys.modules[self.module_name]
|
| 82 |
+
module_path = os.path.abspath(module.__path__[0])
|
| 83 |
+
|
| 84 |
+
# setup the pytest arguments
|
| 85 |
+
pytest_args = ["-l"]
|
| 86 |
+
|
| 87 |
+
# offset verbosity. The "-q" cancels a "-v".
|
| 88 |
+
pytest_args += ["-q"]
|
| 89 |
+
|
| 90 |
+
# Filter out annoying import messages. Want these in both develop and
|
| 91 |
+
# release mode.
|
| 92 |
+
pytest_args += [
|
| 93 |
+
"-W ignore:Not importing directory",
|
| 94 |
+
"-W ignore:numpy.dtype size changed",
|
| 95 |
+
"-W ignore:numpy.ufunc size changed",
|
| 96 |
+
]
|
| 97 |
+
|
| 98 |
+
if doctests:
|
| 99 |
+
raise ValueError("Doctests not supported")
|
| 100 |
+
|
| 101 |
+
if extra_argv:
|
| 102 |
+
pytest_args += list(extra_argv)
|
| 103 |
+
|
| 104 |
+
if verbose > 1:
|
| 105 |
+
pytest_args += ["-" + "v" * (verbose - 1)]
|
| 106 |
+
|
| 107 |
+
if coverage:
|
| 108 |
+
pytest_args += ["--cov=" + module_path]
|
| 109 |
+
|
| 110 |
+
if label == "fast":
|
| 111 |
+
pytest_args += ["-m", "not slow"]
|
| 112 |
+
elif label != "full":
|
| 113 |
+
pytest_args += ["-m", label]
|
| 114 |
+
|
| 115 |
+
if durations >= 0:
|
| 116 |
+
pytest_args += [f"--durations={durations}"]
|
| 117 |
+
|
| 118 |
+
if tests is None:
|
| 119 |
+
tests = [self.module_name]
|
| 120 |
+
|
| 121 |
+
pytest_args += ["--pyargs"] + list(tests)
|
| 122 |
+
|
| 123 |
+
# run tests.
|
| 124 |
+
_show_skimage_info()
|
| 125 |
+
|
| 126 |
+
try:
|
| 127 |
+
code = pytest.main(pytest_args)
|
| 128 |
+
except SystemExit as exc:
|
| 129 |
+
code = exc.code
|
| 130 |
+
|
| 131 |
+
return code == 0
|
skimage/_shared/testing.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Testing utilities.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import re
|
| 7 |
+
import struct
|
| 8 |
+
import threading
|
| 9 |
+
import functools
|
| 10 |
+
import inspect
|
| 11 |
+
from tempfile import NamedTemporaryFile
|
| 12 |
+
|
| 13 |
+
import numpy as np
|
| 14 |
+
from numpy import testing
|
| 15 |
+
from numpy.testing import (
|
| 16 |
+
TestCase,
|
| 17 |
+
assert_,
|
| 18 |
+
assert_warns,
|
| 19 |
+
assert_no_warnings,
|
| 20 |
+
assert_equal,
|
| 21 |
+
assert_almost_equal,
|
| 22 |
+
assert_array_equal,
|
| 23 |
+
assert_allclose,
|
| 24 |
+
assert_array_almost_equal,
|
| 25 |
+
assert_array_almost_equal_nulp,
|
| 26 |
+
assert_array_less,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
from .. import data, io
|
| 30 |
+
from ..data._fetchers import _fetch
|
| 31 |
+
from ..util import img_as_uint, img_as_float, img_as_int, img_as_ubyte
|
| 32 |
+
from ._warnings import expected_warnings
|
| 33 |
+
|
| 34 |
+
import pytest
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
skipif = pytest.mark.skipif
|
| 38 |
+
xfail = pytest.mark.xfail
|
| 39 |
+
parametrize = pytest.mark.parametrize
|
| 40 |
+
raises = pytest.raises
|
| 41 |
+
fixture = pytest.fixture
|
| 42 |
+
|
| 43 |
+
SKIP_RE = re.compile(r"(\s*>>>.*?)(\s*)#\s*skip\s+if\s+(.*)$")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# true if python is running in 32bit mode
|
| 47 |
+
# Calculate the size of a void * pointer in bits
|
| 48 |
+
# https://docs.python.org/3/library/struct.html
|
| 49 |
+
arch32 = struct.calcsize("P") * 8 == 32
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def assert_less(a, b, msg=None):
|
| 53 |
+
message = f"{a!r} is not lower than {b!r}"
|
| 54 |
+
if msg is not None:
|
| 55 |
+
message += ": " + msg
|
| 56 |
+
assert a < b, message
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def assert_greater(a, b, msg=None):
|
| 60 |
+
message = f"{a!r} is not greater than {b!r}"
|
| 61 |
+
if msg is not None:
|
| 62 |
+
message += ": " + msg
|
| 63 |
+
assert a > b, message
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def doctest_skip_parser(func):
|
| 67 |
+
"""Decorator replaces custom skip test markup in doctests
|
| 68 |
+
|
| 69 |
+
Say a function has a docstring::
|
| 70 |
+
|
| 71 |
+
>>> something, HAVE_AMODULE, HAVE_BMODULE = 0, False, False
|
| 72 |
+
>>> something # skip if not HAVE_AMODULE
|
| 73 |
+
0
|
| 74 |
+
>>> something # skip if HAVE_BMODULE
|
| 75 |
+
0
|
| 76 |
+
|
| 77 |
+
This decorator will evaluate the expression after ``skip if``. If this
|
| 78 |
+
evaluates to True, then the comment is replaced by ``# doctest: +SKIP``. If
|
| 79 |
+
False, then the comment is just removed. The expression is evaluated in the
|
| 80 |
+
``globals`` scope of `func`.
|
| 81 |
+
|
| 82 |
+
For example, if the module global ``HAVE_AMODULE`` is False, and module
|
| 83 |
+
global ``HAVE_BMODULE`` is False, the returned function will have docstring::
|
| 84 |
+
|
| 85 |
+
>>> something # doctest: +SKIP
|
| 86 |
+
>>> something + else # doctest: +SKIP
|
| 87 |
+
>>> something # doctest: +SKIP
|
| 88 |
+
|
| 89 |
+
"""
|
| 90 |
+
lines = func.__doc__.split('\n')
|
| 91 |
+
new_lines = []
|
| 92 |
+
for line in lines:
|
| 93 |
+
match = SKIP_RE.match(line)
|
| 94 |
+
if match is None:
|
| 95 |
+
new_lines.append(line)
|
| 96 |
+
continue
|
| 97 |
+
code, space, expr = match.groups()
|
| 98 |
+
|
| 99 |
+
try:
|
| 100 |
+
# Works as a function decorator
|
| 101 |
+
if eval(expr, func.__globals__):
|
| 102 |
+
code = code + space + "# doctest: +SKIP"
|
| 103 |
+
except AttributeError:
|
| 104 |
+
# Works as a class decorator
|
| 105 |
+
if eval(expr, func.__init__.__globals__):
|
| 106 |
+
code = code + space + "# doctest: +SKIP"
|
| 107 |
+
|
| 108 |
+
new_lines.append(code)
|
| 109 |
+
func.__doc__ = "\n".join(new_lines)
|
| 110 |
+
return func
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def roundtrip(image, plugin, suffix):
|
| 114 |
+
"""Save and read an image using a specified plugin"""
|
| 115 |
+
if '.' not in suffix:
|
| 116 |
+
suffix = '.' + suffix
|
| 117 |
+
with NamedTemporaryFile(suffix=suffix, delete=False) as temp_file:
|
| 118 |
+
fname = temp_file.name
|
| 119 |
+
io.imsave(fname, image, plugin=plugin)
|
| 120 |
+
new = io.imread(fname, plugin=plugin)
|
| 121 |
+
try:
|
| 122 |
+
os.remove(fname)
|
| 123 |
+
except Exception:
|
| 124 |
+
pass
|
| 125 |
+
return new
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def color_check(plugin, fmt='png'):
|
| 129 |
+
"""Check roundtrip behavior for color images.
|
| 130 |
+
|
| 131 |
+
All major input types should be handled as ubytes and read
|
| 132 |
+
back correctly.
|
| 133 |
+
"""
|
| 134 |
+
img = img_as_ubyte(data.chelsea())
|
| 135 |
+
r1 = roundtrip(img, plugin, fmt)
|
| 136 |
+
testing.assert_allclose(img, r1)
|
| 137 |
+
|
| 138 |
+
img2 = img > 128
|
| 139 |
+
r2 = roundtrip(img2, plugin, fmt)
|
| 140 |
+
testing.assert_allclose(img2, r2.astype(bool))
|
| 141 |
+
|
| 142 |
+
img3 = img_as_float(img)
|
| 143 |
+
r3 = roundtrip(img3, plugin, fmt)
|
| 144 |
+
testing.assert_allclose(r3, img)
|
| 145 |
+
|
| 146 |
+
img4 = img_as_int(img)
|
| 147 |
+
if fmt.lower() in (('tif', 'tiff')):
|
| 148 |
+
img4 -= 100
|
| 149 |
+
r4 = roundtrip(img4, plugin, fmt)
|
| 150 |
+
testing.assert_allclose(r4, img4)
|
| 151 |
+
else:
|
| 152 |
+
r4 = roundtrip(img4, plugin, fmt)
|
| 153 |
+
testing.assert_allclose(r4, img_as_ubyte(img4))
|
| 154 |
+
|
| 155 |
+
img5 = img_as_uint(img)
|
| 156 |
+
r5 = roundtrip(img5, plugin, fmt)
|
| 157 |
+
testing.assert_allclose(r5, img)
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def mono_check(plugin, fmt='png'):
|
| 161 |
+
"""Check the roundtrip behavior for images that support most types.
|
| 162 |
+
|
| 163 |
+
All major input types should be handled.
|
| 164 |
+
"""
|
| 165 |
+
|
| 166 |
+
img = img_as_ubyte(data.moon())
|
| 167 |
+
r1 = roundtrip(img, plugin, fmt)
|
| 168 |
+
testing.assert_allclose(img, r1)
|
| 169 |
+
|
| 170 |
+
img2 = img > 128
|
| 171 |
+
r2 = roundtrip(img2, plugin, fmt)
|
| 172 |
+
testing.assert_allclose(img2, r2.astype(bool))
|
| 173 |
+
|
| 174 |
+
img3 = img_as_float(img)
|
| 175 |
+
r3 = roundtrip(img3, plugin, fmt)
|
| 176 |
+
if r3.dtype.kind == 'f':
|
| 177 |
+
testing.assert_allclose(img3, r3)
|
| 178 |
+
else:
|
| 179 |
+
testing.assert_allclose(r3, img_as_uint(img))
|
| 180 |
+
|
| 181 |
+
img4 = img_as_int(img)
|
| 182 |
+
if fmt.lower() in (('tif', 'tiff')):
|
| 183 |
+
img4 -= 100
|
| 184 |
+
r4 = roundtrip(img4, plugin, fmt)
|
| 185 |
+
testing.assert_allclose(r4, img4)
|
| 186 |
+
else:
|
| 187 |
+
r4 = roundtrip(img4, plugin, fmt)
|
| 188 |
+
testing.assert_allclose(r4, img_as_uint(img4))
|
| 189 |
+
|
| 190 |
+
img5 = img_as_uint(img)
|
| 191 |
+
r5 = roundtrip(img5, plugin, fmt)
|
| 192 |
+
testing.assert_allclose(r5, img5)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def fetch(data_filename):
|
| 196 |
+
"""Attempt to fetch data, but if unavailable, skip the tests."""
|
| 197 |
+
try:
|
| 198 |
+
return _fetch(data_filename)
|
| 199 |
+
except (ConnectionError, ModuleNotFoundError):
|
| 200 |
+
pytest.skip(f'Unable to download {data_filename}', allow_module_level=True)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def run_in_parallel(num_threads=2, warnings_matching=None):
|
| 204 |
+
"""Decorator to run the same function multiple times in parallel.
|
| 205 |
+
|
| 206 |
+
This decorator is useful to ensure that separate threads execute
|
| 207 |
+
concurrently and correctly while releasing the GIL.
|
| 208 |
+
|
| 209 |
+
Parameters
|
| 210 |
+
----------
|
| 211 |
+
num_threads : int, optional
|
| 212 |
+
The number of times the function is run in parallel.
|
| 213 |
+
|
| 214 |
+
warnings_matching: list or None
|
| 215 |
+
This parameter is passed on to `expected_warnings` so as not to have
|
| 216 |
+
race conditions with the warnings filters. A single
|
| 217 |
+
`expected_warnings` context manager is used for all threads.
|
| 218 |
+
If None, then no warnings are checked.
|
| 219 |
+
|
| 220 |
+
"""
|
| 221 |
+
|
| 222 |
+
assert num_threads > 0
|
| 223 |
+
|
| 224 |
+
def wrapper(func):
|
| 225 |
+
@functools.wraps(func)
|
| 226 |
+
def inner(*args, **kwargs):
|
| 227 |
+
with expected_warnings(warnings_matching):
|
| 228 |
+
threads = []
|
| 229 |
+
for i in range(num_threads - 1):
|
| 230 |
+
thread = threading.Thread(target=func, args=args, kwargs=kwargs)
|
| 231 |
+
threads.append(thread)
|
| 232 |
+
for thread in threads:
|
| 233 |
+
thread.start()
|
| 234 |
+
|
| 235 |
+
func(*args, **kwargs)
|
| 236 |
+
|
| 237 |
+
for thread in threads:
|
| 238 |
+
thread.join()
|
| 239 |
+
|
| 240 |
+
return inner
|
| 241 |
+
|
| 242 |
+
return wrapper
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
def assert_stacklevel(warnings, *, offset=-1):
|
| 246 |
+
"""Assert correct stacklevel of captured warnings.
|
| 247 |
+
|
| 248 |
+
When scikit-image raises warnings, the stacklevel should ideally be set
|
| 249 |
+
so that the origin of the warnings will point to the public function
|
| 250 |
+
that was called by the user and not necessarily the very place where the
|
| 251 |
+
warnings were emitted (which may be inside of some internal function).
|
| 252 |
+
This utility function helps with checking that
|
| 253 |
+
the stacklevel was set correctly on warnings captured by `pytest.warns`.
|
| 254 |
+
|
| 255 |
+
Parameters
|
| 256 |
+
----------
|
| 257 |
+
warnings : collections.abc.Iterable[warning.WarningMessage]
|
| 258 |
+
Warnings that were captured by `pytest.warns`.
|
| 259 |
+
offset : int, optional
|
| 260 |
+
Offset from the line this function is called to the line were the
|
| 261 |
+
warning is supposed to originate from. For multiline calls, the
|
| 262 |
+
first line is relevant. Defaults to -1 which corresponds to the line
|
| 263 |
+
right above the one where this function is called.
|
| 264 |
+
|
| 265 |
+
Raises
|
| 266 |
+
------
|
| 267 |
+
AssertionError
|
| 268 |
+
If a warning in `warnings` does not match the expected line number or
|
| 269 |
+
file name.
|
| 270 |
+
|
| 271 |
+
Examples
|
| 272 |
+
--------
|
| 273 |
+
>>> def test_something():
|
| 274 |
+
... with pytest.warns(UserWarning, match="some message") as record:
|
| 275 |
+
... something_raising_a_warning()
|
| 276 |
+
... assert_stacklevel(record)
|
| 277 |
+
...
|
| 278 |
+
>>> def test_another_thing():
|
| 279 |
+
... with pytest.warns(UserWarning, match="some message") as record:
|
| 280 |
+
... iam_raising_many_warnings(
|
| 281 |
+
... "A long argument that forces the call to wrap."
|
| 282 |
+
... )
|
| 283 |
+
... assert_stacklevel(record, offset=-3)
|
| 284 |
+
"""
|
| 285 |
+
frame = inspect.stack()[1].frame # 0 is current frame, 1 is outer frame
|
| 286 |
+
line_number = frame.f_lineno + offset
|
| 287 |
+
filename = frame.f_code.co_filename
|
| 288 |
+
expected = f"{filename}:{line_number}"
|
| 289 |
+
for warning in warnings:
|
| 290 |
+
actual = f"{warning.filename}:{warning.lineno}"
|
| 291 |
+
assert actual == expected, f"{actual} != {expected}"
|
skimage/_shared/tests/__init__.py
ADDED
|
File without changes
|
skimage/_shared/tests/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (159 Bytes). View file
|
|
|
skimage/_shared/tests/__pycache__/test_coord.cpython-310.pyc
ADDED
|
Binary file (2.45 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_dtype.cpython-310.pyc
ADDED
|
Binary file (761 Bytes). View file
|
|
|
skimage/_shared/tests/__pycache__/test_fast_exp.cpython-310.pyc
ADDED
|
Binary file (820 Bytes). View file
|
|
|
skimage/_shared/tests/__pycache__/test_geometry.cpython-310.pyc
ADDED
|
Binary file (2.27 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_interpolation.cpython-310.pyc
ADDED
|
Binary file (1.69 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_safe_as_int.cpython-310.pyc
ADDED
|
Binary file (1.69 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_testing.cpython-310.pyc
ADDED
|
Binary file (4.94 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_utils.cpython-310.pyc
ADDED
|
Binary file (14.5 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_version_requirements.cpython-310.pyc
ADDED
|
Binary file (1.56 kB). View file
|
|
|
skimage/_shared/tests/__pycache__/test_warnings.cpython-310.pyc
ADDED
|
Binary file (1.4 kB). View file
|
|
|
skimage/_shared/tests/test_coord.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pytest
|
| 5 |
+
from scipy.spatial.distance import pdist, minkowski
|
| 6 |
+
|
| 7 |
+
from skimage._shared.coord import ensure_spacing
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@pytest.mark.parametrize("p", [1, 2, np.inf])
|
| 11 |
+
@pytest.mark.parametrize("size", [30, 50, None])
|
| 12 |
+
def test_ensure_spacing_trivial(p, size):
|
| 13 |
+
# --- Empty input
|
| 14 |
+
assert ensure_spacing([], p_norm=p) == []
|
| 15 |
+
|
| 16 |
+
# --- A unique point
|
| 17 |
+
coord = np.random.randn(1, 2)
|
| 18 |
+
assert np.array_equal(coord, ensure_spacing(coord, p_norm=p, min_split_size=size))
|
| 19 |
+
|
| 20 |
+
# --- Verified spacing
|
| 21 |
+
coord = np.random.randn(100, 2)
|
| 22 |
+
|
| 23 |
+
# --- 0 spacing
|
| 24 |
+
assert np.array_equal(
|
| 25 |
+
coord, ensure_spacing(coord, spacing=0, p_norm=p, min_split_size=size)
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
# Spacing is chosen to be half the minimum distance
|
| 29 |
+
spacing = pdist(coord, metric=minkowski, p=p).min() * 0.5
|
| 30 |
+
|
| 31 |
+
out = ensure_spacing(coord, spacing=spacing, p_norm=p, min_split_size=size)
|
| 32 |
+
|
| 33 |
+
assert np.array_equal(coord, out)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@pytest.mark.parametrize("ndim", [1, 2, 3, 4, 5])
|
| 37 |
+
@pytest.mark.parametrize("size", [2, 10, None])
|
| 38 |
+
def test_ensure_spacing_nD(ndim, size):
|
| 39 |
+
coord = np.ones((5, ndim))
|
| 40 |
+
|
| 41 |
+
expected = np.ones((1, ndim))
|
| 42 |
+
|
| 43 |
+
assert np.array_equal(ensure_spacing(coord, min_split_size=size), expected)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@pytest.mark.parametrize("p", [1, 2, np.inf])
|
| 47 |
+
@pytest.mark.parametrize("size", [50, 100, None])
|
| 48 |
+
def test_ensure_spacing_batch_processing(p, size):
|
| 49 |
+
coord = np.random.randn(100, 2)
|
| 50 |
+
|
| 51 |
+
# --- Consider the average distance btween the point as spacing
|
| 52 |
+
spacing = np.median(pdist(coord, metric=minkowski, p=p))
|
| 53 |
+
|
| 54 |
+
expected = ensure_spacing(coord, spacing=spacing, p_norm=p)
|
| 55 |
+
|
| 56 |
+
assert np.array_equal(
|
| 57 |
+
ensure_spacing(coord, spacing=spacing, p_norm=p, min_split_size=size), expected
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def test_max_batch_size():
|
| 62 |
+
"""Small batches are slow, large batches -> large allocations -> also slow.
|
| 63 |
+
|
| 64 |
+
https://github.com/scikit-image/scikit-image/pull/6035#discussion_r751518691
|
| 65 |
+
"""
|
| 66 |
+
coords = np.random.randint(low=0, high=1848, size=(40000, 2))
|
| 67 |
+
tstart = time.time()
|
| 68 |
+
ensure_spacing(coords, spacing=100, min_split_size=50, max_split_size=2000)
|
| 69 |
+
dur1 = time.time() - tstart
|
| 70 |
+
|
| 71 |
+
tstart = time.time()
|
| 72 |
+
ensure_spacing(coords, spacing=100, min_split_size=50, max_split_size=20000)
|
| 73 |
+
dur2 = time.time() - tstart
|
| 74 |
+
|
| 75 |
+
# Originally checked dur1 < dur2 to assert that the default batch size was
|
| 76 |
+
# faster than a much larger batch size. However, on rare occasion a CI test
|
| 77 |
+
# case would fail with dur1 ~5% larger than dur2. To be more robust to
|
| 78 |
+
# variable load or differences across architectures, we relax this here.
|
| 79 |
+
assert dur1 < 1.33 * dur2
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@pytest.mark.parametrize("p", [1, 2, np.inf])
|
| 83 |
+
@pytest.mark.parametrize("size", [30, 50, None])
|
| 84 |
+
def test_ensure_spacing_p_norm(p, size):
|
| 85 |
+
coord = np.random.randn(100, 2)
|
| 86 |
+
|
| 87 |
+
# --- Consider the average distance btween the point as spacing
|
| 88 |
+
spacing = np.median(pdist(coord, metric=minkowski, p=p))
|
| 89 |
+
out = ensure_spacing(coord, spacing=spacing, p_norm=p, min_split_size=size)
|
| 90 |
+
|
| 91 |
+
assert pdist(out, metric=minkowski, p=p).min() > spacing
|
skimage/_shared/tests/test_dtype.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
from ..dtype import numeric_dtype_min_max, numeric_types
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class Test_numeric_dtype_min_max:
|
| 9 |
+
@pytest.mark.parametrize("dtype", numeric_types)
|
| 10 |
+
def test_all_numeric_types(self, dtype):
|
| 11 |
+
min_, max_ = numeric_dtype_min_max(dtype)
|
| 12 |
+
assert np.isscalar(min_)
|
| 13 |
+
assert np.isscalar(max_)
|
| 14 |
+
assert min_ < max_
|