imops 0.8.8__cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. _build_utils.py +113 -0
  2. imops/__init__.py +10 -0
  3. imops/__version__.py +1 -0
  4. imops/_configs.py +29 -0
  5. imops/backend.py +95 -0
  6. imops/box.py +74 -0
  7. imops/cpp/cpp_modules.cpython-38-i386-linux-gnu.so +0 -0
  8. imops/cpp/interp2d/delaunator/delaunator-header-only.hpp +33 -0
  9. imops/cpp/interp2d/delaunator/delaunator.cpp +645 -0
  10. imops/cpp/interp2d/delaunator/delaunator.hpp +170 -0
  11. imops/cpp/interp2d/interpolator.h +52 -0
  12. imops/cpp/interp2d/triangulator.h +198 -0
  13. imops/cpp/interp2d/utils.h +63 -0
  14. imops/cpp/main.cpp +13 -0
  15. imops/crop.py +120 -0
  16. imops/interp1d.py +207 -0
  17. imops/interp2d.py +120 -0
  18. imops/measure.py +228 -0
  19. imops/morphology.py +525 -0
  20. imops/numeric.py +384 -0
  21. imops/pad.py +253 -0
  22. imops/py.typed +0 -0
  23. imops/radon.py +247 -0
  24. imops/src/__init__.py +0 -0
  25. imops/src/_backprojection.c +27339 -0
  26. imops/src/_backprojection.cpython-38-i386-linux-gnu.so +0 -0
  27. imops/src/_fast_backprojection.c +27339 -0
  28. imops/src/_fast_backprojection.cpython-38-i386-linux-gnu.so +0 -0
  29. imops/src/_fast_measure.c +33810 -0
  30. imops/src/_fast_measure.cpython-38-i386-linux-gnu.so +0 -0
  31. imops/src/_fast_morphology.c +26089 -0
  32. imops/src/_fast_morphology.cpython-38-i386-linux-gnu.so +0 -0
  33. imops/src/_fast_numeric.c +48651 -0
  34. imops/src/_fast_numeric.cpython-38-i386-linux-gnu.so +0 -0
  35. imops/src/_fast_radon.c +30714 -0
  36. imops/src/_fast_radon.cpython-38-i386-linux-gnu.so +0 -0
  37. imops/src/_fast_zoom.c +57203 -0
  38. imops/src/_fast_zoom.cpython-38-i386-linux-gnu.so +0 -0
  39. imops/src/_measure.c +33810 -0
  40. imops/src/_measure.cpython-38-i386-linux-gnu.so +0 -0
  41. imops/src/_morphology.c +26089 -0
  42. imops/src/_morphology.cpython-38-i386-linux-gnu.so +0 -0
  43. imops/src/_numba_zoom.py +503 -0
  44. imops/src/_numeric.c +48651 -0
  45. imops/src/_numeric.cpython-38-i386-linux-gnu.so +0 -0
  46. imops/src/_radon.c +30714 -0
  47. imops/src/_radon.cpython-38-i386-linux-gnu.so +0 -0
  48. imops/src/_zoom.c +57203 -0
  49. imops/src/_zoom.cpython-38-i386-linux-gnu.so +0 -0
  50. imops/testing.py +57 -0
  51. imops/utils.py +205 -0
  52. imops/zoom.py +297 -0
  53. imops-0.8.8.dist-info/LICENSE +21 -0
  54. imops-0.8.8.dist-info/METADATA +218 -0
  55. imops-0.8.8.dist-info/RECORD +58 -0
  56. imops-0.8.8.dist-info/WHEEL +6 -0
  57. imops-0.8.8.dist-info/top_level.txt +2 -0
  58. imops.libs/libgomp-65f46eca.so.1.0.0 +0 -0
_build_utils.py ADDED
@@ -0,0 +1,113 @@
1
+ import platform
2
+ import shutil
3
+ from pathlib import Path
4
+
5
+ from setuptools import Extension
6
+ from setuptools.command.build_py import build_py
7
+
8
+
9
+ class LazyImport(dict):
10
+ """Hacky way to return any module's `include` path with lazy import."""
11
+
12
+ # Must be json-serializable due to
13
+ # https://github.com/cython/cython/blob/6ad6ca0e9e7d030354b7fe7d7b56c3f6e6a4bc23/Cython/Compiler/ModuleNode.py#L773
14
+ def __init__(self, module_name):
15
+ self.module_name = module_name
16
+ super().__init__(self, description=self.__doc__)
17
+
18
+ # Must be hashable due to
19
+ # https://github.com/cython/cython/blob/6ad6ca0e9e7d030354b7fe7d7b56c3f6e6a4bc23/Cython/Compiler/Main.py#L307
20
+ def __hash__(self):
21
+ return id(self)
22
+
23
+ def __repr__(self):
24
+ scope = {}
25
+ exec(f'import {self.module_name} as module', scope)
26
+
27
+ return scope['module'].get_include()
28
+
29
+ __fspath__ = __repr__
30
+
31
+
32
+ class NumpyLibImport(str):
33
+ """Hacky way to return Numpy's `lib` path with lazy import."""
34
+
35
+ # Exploit of https://github.com/pypa/setuptools/blob/1ef36f2d336e239bd8f83507cb9447e060b6ed60/setuptools/_distutils/
36
+ # unixccompiler.py#L276-L277
37
+ def __radd__(self, left):
38
+ import numpy as np
39
+
40
+ return left + str(Path(np.get_include()).parent / 'lib')
41
+
42
+ def __hash__(self):
43
+ return id(self)
44
+
45
+
46
+ class PyprojectBuild(build_py):
47
+ def run(self):
48
+ self.run_command('build_ext')
49
+ return super().run()
50
+
51
+ def initialize_options(self):
52
+ super().initialize_options()
53
+
54
+ self.distribution.ext_modules = get_ext_modules()
55
+
56
+
57
+ def get_ext_modules():
58
+ name = 'imops'
59
+ on_windows = platform.system() == 'Windows'
60
+ args = ['/openmp' if on_windows else '-fopenmp']
61
+ cpp_args = [
62
+ '/std:c++20' if on_windows else '-std=c++2a',
63
+ '/O3' if on_windows else '-O3',
64
+ ] # FIXME: account for higher gcc versions
65
+
66
+ modules = ['backprojection', 'measure', 'morphology', 'numeric', 'radon', 'zoom']
67
+ modules_to_link_against_numpy_core_math_lib = ['numeric']
68
+
69
+ src_dir = Path(__file__).parent / name / 'src'
70
+ for module in modules:
71
+ # Cython extension and .pyx source file names must be the same to compile
72
+ # https://stackoverflow.com/questions/8024805/cython-compiled-c-extension-importerror-dynamic-module-does-not-define-init-fu
73
+ shutil.copyfile(src_dir / f'_{module}.pyx', src_dir / f'_fast_{module}.pyx')
74
+
75
+ extensions = [
76
+ Extension(
77
+ f'{name}.cpp.cpp_modules',
78
+ [f'{name}/cpp/main.cpp'],
79
+ include_dirs=[LazyImport('pybind11')],
80
+ extra_compile_args=args + cpp_args,
81
+ extra_link_args=args + cpp_args,
82
+ language='c++',
83
+ )
84
+ ]
85
+ for module in modules:
86
+ libraries = []
87
+ library_dirs = []
88
+ include_dirs = [LazyImport('numpy')]
89
+
90
+ if module in modules_to_link_against_numpy_core_math_lib:
91
+ library_dirs.append(NumpyLibImport())
92
+ libraries.append('npymath')
93
+ if not on_windows:
94
+ libraries.append('m')
95
+
96
+ # FIXME: import of `ffast-math` compiled modules changes global FPU state, so now `fast=True` will just
97
+ # fallback to standard `-O2` compiled versions until https://github.com/neuro-ml/imops/issues/37 is resolved
98
+ # for prefix, additional_args in zip(['', 'fast_'], [[], ['-ffast-math']])
99
+ for prefix, additional_args in zip(['', 'fast_'], [[], []]):
100
+ extensions.append(
101
+ Extension(
102
+ f'{name}.src._{prefix}{module}',
103
+ [f'{name}/src/_{prefix}{module}.pyx'],
104
+ include_dirs=include_dirs,
105
+ library_dirs=library_dirs,
106
+ libraries=libraries,
107
+ extra_compile_args=args + additional_args,
108
+ extra_link_args=args + additional_args,
109
+ define_macros=[('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION')],
110
+ )
111
+ )
112
+
113
+ return extensions
imops/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from .__version__ import __version__
2
+ from .backend import Cython, Numba, Scipy, imops_backend, set_backend
3
+ from .crop import crop_to_box, crop_to_shape
4
+ from .interp1d import interp1d
5
+ from .measure import label
6
+ from .morphology import binary_closing, binary_dilation, binary_erosion, binary_opening
7
+ from .numeric import copy, fill_, full, pointwise_add
8
+ from .pad import pad, pad_to_divisible, pad_to_shape, restore_crop
9
+ from .radon import inverse_radon, radon
10
+ from .zoom import _zoom, zoom, zoom_to_shape
imops/__version__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = '0.8.8'
imops/_configs.py ADDED
@@ -0,0 +1,29 @@
1
+ from itertools import product
2
+
3
+ from .backend import Cython, Numba, Scipy
4
+
5
+
6
+ scipy_configs = [Scipy()]
7
+ radon_configs = [Cython(fast) for fast in [False, True]]
8
+ numeric_configs = [
9
+ Scipy(),
10
+ *[Cython(fast) for fast in [False, True]],
11
+ ]
12
+ measure_configs = [
13
+ Scipy(),
14
+ *[Cython(fast) for fast in [False, True]],
15
+ ]
16
+ morphology_configs = [
17
+ Scipy(),
18
+ *[Cython(fast) for fast in [False, True]],
19
+ ]
20
+ zoom_configs = [
21
+ Scipy(),
22
+ *[Cython(fast) for fast in [False, True]],
23
+ *[Numba(*flags) for flags in product([False, True], repeat=3)],
24
+ ]
25
+ interp1d_configs = [
26
+ Scipy(),
27
+ *[Cython(fast) for fast in [False, True]],
28
+ *[Numba(*flags) for flags in product([False, True], repeat=3)],
29
+ ]
imops/backend.py ADDED
@@ -0,0 +1,95 @@
1
+ from contextlib import contextmanager
2
+ from dataclasses import dataclass
3
+ from typing import Dict, Type, Union
4
+ from warnings import warn
5
+
6
+
7
+ class Backend:
8
+ def __init_subclass__(cls, **kwargs):
9
+ name = cls.__name__
10
+ if name in _AVAILABLE_BACKENDS:
11
+ raise ValueError(f'The name "{name}" is already in use.')
12
+ _AVAILABLE_BACKENDS[name] = cls
13
+ if not hasattr(Backend, name):
14
+ setattr(Backend, name, cls)
15
+
16
+ @property
17
+ def name(self):
18
+ return type(self).__name__
19
+
20
+ Cython: 'Cython'
21
+ Numba: 'Numba'
22
+ Scipy: 'Scipy'
23
+
24
+
25
+ BackendLike = Union[str, Backend, Type[Backend], None]
26
+ _AVAILABLE_BACKENDS: Dict[str, Type[Backend]] = {}
27
+
28
+
29
+ def resolve_backend(value: BackendLike, warn_stacklevel: int = 1) -> Backend:
30
+ if value is None:
31
+ return DEFAULT_BACKEND
32
+
33
+ if isinstance(value, str):
34
+ if value not in _AVAILABLE_BACKENDS:
35
+ raise ValueError(f'"{value}" is not in the list of available backends: {tuple(_AVAILABLE_BACKENDS)}.')
36
+
37
+ return _AVAILABLE_BACKENDS[value]()
38
+
39
+ if isinstance(value, type):
40
+ value = value()
41
+
42
+ if not isinstance(value, Backend):
43
+ raise ValueError(f'Expected a `Backend` instance, got {value}.')
44
+
45
+ if isinstance(value, Cython) and value.fast:
46
+ warn('`fast=True` has no effect for `Cython` backend for now.', stacklevel=warn_stacklevel)
47
+
48
+ return value
49
+
50
+
51
+ def set_backend(backend: BackendLike) -> Backend:
52
+ global DEFAULT_BACKEND
53
+ current = DEFAULT_BACKEND
54
+ DEFAULT_BACKEND = resolve_backend(backend)
55
+ return current
56
+
57
+
58
+ @contextmanager
59
+ def imops_backend(backend: BackendLike):
60
+ previous = set_backend(backend)
61
+ try:
62
+ yield
63
+ finally:
64
+ set_backend(previous)
65
+
66
+
67
+ # implementations
68
+ # TODO: Investigate whether it is safe to use -ffast-math in numba
69
+ @dataclass(frozen=True)
70
+ class Numba(Backend):
71
+ parallel: bool = True
72
+ nogil: bool = True
73
+ cache: bool = True
74
+
75
+ def __post_init__(self):
76
+ try:
77
+ import numba # noqa: F401
78
+ except ModuleNotFoundError: # pragma: no cover
79
+ raise ModuleNotFoundError('Install `numba` package (pip install numba) to use "numba" backend.')
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class Cython(Backend):
84
+ fast: bool = False
85
+
86
+
87
+ @dataclass(frozen=True)
88
+ class Scipy(Backend):
89
+ pass
90
+
91
+
92
+ DEFAULT_BACKEND = Cython()
93
+
94
+ BACKEND_NAME2ENV_NUM_THREADS_VAR_NAME = {Cython.__name__: 'OMP_NUM_THREADS', Numba.__name__: 'NUMBA_NUM_THREADS'}
95
+ SINGLE_THREADED_BACKENDS = (Scipy.__name__,)
imops/box.py ADDED
@@ -0,0 +1,74 @@
1
+ import itertools
2
+ from copy import copy
3
+ from functools import wraps
4
+ from typing import Callable, Tuple
5
+
6
+ import numpy as np
7
+
8
+ # for backward compatibility
9
+ from .utils import build_slices # noqa: F401
10
+
11
+
12
+ # Immutable numpy array
13
+ Box = np.ndarray
14
+
15
+
16
+ def make_box(iterable) -> Box:
17
+ """Returns a box, generated from copy of the `iterable`."""
18
+ box = np.asarray(copy(iterable))
19
+ box.setflags(write=False)
20
+
21
+ assert box.ndim == 2 and len(box) == 2, box.shape
22
+ assert np.all(box[0] <= box[1]), box
23
+
24
+ return box
25
+
26
+
27
+ def returns_box(func: Callable) -> Callable:
28
+ """Returns function, decorated so that it returns a box."""
29
+
30
+ @wraps(func)
31
+ def func_returning_box(*args, **kwargs):
32
+ return make_box(func(*args, **kwargs))
33
+
34
+ func_returning_box.__annotations__['return'] = Box
35
+
36
+ return func_returning_box
37
+
38
+
39
+ @returns_box
40
+ def mask_to_box(mask: np.ndarray) -> Box:
41
+ """Find the smallest box that contains all true values of the `mask`."""
42
+ if not mask.any():
43
+ raise ValueError('The mask is empty.')
44
+
45
+ start, stop = [], []
46
+ for ax in itertools.combinations(range(mask.ndim), mask.ndim - 1):
47
+ nonzero = np.any(mask, axis=ax)
48
+ if np.any(nonzero):
49
+ left, right = np.where(nonzero)[0][[0, -1]]
50
+ else:
51
+ left, right = 0, 0
52
+ start.insert(0, left)
53
+ stop.insert(0, right + 1)
54
+
55
+ return start, stop
56
+
57
+
58
+ @returns_box
59
+ def shape_to_box(shape: Tuple) -> Box:
60
+ return make_box([(0,) * len(shape), shape]) # fmt: skip
61
+
62
+
63
+ def box_to_shape(box: Box) -> Tuple:
64
+ return tuple(box[1] - box[0])
65
+
66
+
67
+ @returns_box
68
+ def add_margin(box: Box, margin) -> Box:
69
+ """
70
+ Returns a box with size increased by the ``margin`` (need to be broadcastable to the box)
71
+ compared to the input ``box``.
72
+ """
73
+ margin = np.broadcast_to(margin, box.shape)
74
+ return box[0] - margin[0], box[1] + margin[1]
@@ -0,0 +1,33 @@
1
+ // MIT License
2
+
3
+ // Copyright (c) 2018 Volodymyr Bilonenko
4
+
5
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ // of this software and associated documentation files (the "Software"), to deal
7
+ // in the Software without restriction, including without limitation the rights
8
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ // copies of the Software, and to permit persons to whom the Software is
10
+ // furnished to do so, subject to the following conditions:
11
+
12
+ // The above copyright notice and this permission notice shall be included in all
13
+ // copies or substantial portions of the Software.
14
+
15
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ // SOFTWARE.
22
+
23
+
24
+
25
+ #pragma once
26
+
27
+ #define DELAUNATOR_HEADER_ONLY
28
+
29
+ #include "delaunator.hpp"
30
+
31
+ #include "delaunator.cpp"
32
+
33
+ #undef DELAUNATOR_HEADER_ONLY