Source code for pyxu.util.misc

  1import collections.abc as cabc
  2import importlib
  3import inspect
  4import types
  5
  6import pyxu.info.deps as pxd
  7import pyxu.info.ptype as pxt
  8
  9__all__ = [
 10    "copy_if_unsafe",
 11    "import_module",
 12    "parse_params",
 13    "read_only",
 14]
 15
 16
[docs] 17def peaks(x: pxt.NDArray, y: pxt.NDArray) -> pxt.NDArray: 18 r""" 19 Matlab 2D peaks function. 20 21 Peaks is a function of two variables, obtained by translating and scaling Gaussian distributions. (See `Matlab's 22 peaks function <https://www.mathworks.com/help/matlab/ref/peaks.html>`_.) 23 24 This function is useful for testing purposes. 25 26 Parameters 27 ---------- 28 x: NDArray 29 X coordinates. 30 y: NDArray 31 Y coordinates. 32 33 Returns 34 ------- 35 z: NDArray 36 Values of the 2D function ``peaks`` at the points specified by the entries of `x` and `y`. 37 38 Examples 39 -------- 40 41 .. plot:: 42 43 import numpy as np 44 import matplotlib.pyplot as plt 45 from pyxu.util.misc import peaks 46 47 x = np.linspace(-3, 3, 1000) 48 xx, yy = np.meshgrid(x, x) 49 z = peaks(xx, yy) 50 plt.figure() 51 plt.imshow(z) 52 """ 53 ndi = pxd.NDArrayInfo.from_obj(x) 54 xp = ndi.module() 55 56 a = 3 * ((1 - x) ** 2) * xp.exp(-(x**2) - (y + 1) ** 2) 57 b = -10 * ((x / 5) - x**3 - y**5) * xp.exp(-(x**2) - (y**2)) 58 c = -xp.exp(-((x + 1) ** 2) - (y**2)) / 3 59 z = a + b + c 60 return z
61 62
[docs] 63def star_like_sample( 64 N: pxt.Integer, 65 w: pxt.Integer, 66 s: pxt.Real, 67 po: pxt.Integer, 68 x0: pxt.Real, 69 ndi: pxd.NDArrayInfo = pxd.NDArrayInfo.NUMPY, 70) -> pxt.NDArray: 71 r""" 72 Star-like test image. 73 74 Generates a (N, N) square image of a star-like object normalized between 0 and 1. Based on `GlobalBioIm's 75 StarLikeSample function 76 <https://github.com/Biomedical-Imaging-Group/GlobalBioIm/blob/master/Util/StarLikeSample.m>`_. This function is 77 useful for testing purposes as it contains high-frequency information. 78 79 Parameters 80 ---------- 81 N: Integer 82 Size of the image (must be an even number). 83 w: Integer 84 The number of branches of the sample will be 4*w. 85 s: Real 86 Slope of the sigmoid function :math:`\frac1{1+\exp[s (x-x_{0})]}` attenuating the boundaries. 87 po: Integer 88 Power-raising factor for the final image (to have smoother edges). 89 x0: Real 90 Radial shift of the sigmoid function :math:`\frac1{1+\exp[s (x-x_{0})]}`. 91 ndi: NDArrayInfo 92 Desired array module for the output. 93 94 Returns 95 ------- 96 image: NDArray 97 (N, N) image of star-like sample. 98 99 Examples 100 -------- 101 .. plot:: 102 103 import numpy as np 104 import matplotlib.pyplot as plt 105 from pyxu.util.misc import star_like_sample 106 107 star = star_like_sample(N=256, w=8, s=20, po=3, x0=0.7) 108 plt.figure() 109 plt.imshow(star) 110 """ 111 assert N % 2 == 0 112 xp = ndi.module() 113 114 grid = xp.linspace(-1, 1, N) 115 x, y = xp.meshgrid(grid, grid) 116 theta = xp.arctan2(y, x) 117 image = 1 + xp.cos(4 * w * theta) 118 image /= 1 + xp.exp(s * (xp.sqrt(x**2 + y**2) - x0)) 119 image = xp.maximum(image, 0) / 2 120 image **= po 121 image /= xp.amax(image) 122 return image
123 124
[docs] 125def parse_params(func, *args, **kwargs) -> cabc.Mapping: 126 """ 127 Get function parameterization. 128 129 Returns 130 ------- 131 params: ~collections.abc.Mapping 132 (key, value) params as seen in body of `func` when called via `func(*args, **kwargs)`. 133 """ 134 sig = inspect.Signature.from_callable(func) 135 f_args = sig.bind(*args, **kwargs) 136 f_args.apply_defaults() 137 138 params = dict( 139 zip(f_args.arguments.keys(), f_args.args), # positional arguments 140 **f_args.kwargs, 141 ) 142 return params
143 144
[docs] 145def import_module(name: str, fail_on_error: bool = True) -> types.ModuleType: 146 """ 147 Load a Python module dynamically. 148 """ 149 try: 150 pkg = importlib.import_module(name) 151 except ModuleNotFoundError: 152 if fail_on_error: 153 raise 154 else: 155 pkg = None 156 return pkg
157 158
[docs] 159def copy_if_unsafe(x: pxt.NDArray) -> pxt.NDArray: 160 """ 161 Copy array if it is unsafe to do in-place updates on it. 162 163 In-place updates are unsafe if: 164 165 * the array is read-only, OR 166 * the array is a view onto another array. 167 168 Parameters 169 ---------- 170 x: NDArray 171 172 Returns 173 ------- 174 y: NDArray 175 """ 176 N = pxd.NDArrayInfo 177 ndi = N.from_obj(x) 178 if ndi == N.DASK: 179 # Dask operations span a graph -> always safe. 180 y = x 181 elif ndi == N.NUMPY: 182 read_only = not x.flags.writeable 183 reference = not x.flags.owndata 184 y = x.copy() if (read_only or reference) else x 185 elif ndi == N.CUPY: 186 read_only = False # https://github.com/cupy/cupy/issues/2616 187 reference = not x.flags.owndata 188 y = x.copy() if (read_only or reference) else x 189 else: 190 msg = f"copy_if_unsafe() not yet defined for {ndi}." 191 raise NotImplementedError(msg) 192 return y
193 194
[docs] 195def read_only(x: pxt.NDArray) -> pxt.NDArray: 196 """ 197 Make an array read-only. 198 199 Parameters 200 ---------- 201 x: NDArray 202 203 Returns 204 ------- 205 y: NDArray 206 """ 207 N = pxd.NDArrayInfo 208 ndi = N.from_obj(x) 209 if ndi == N.DASK: 210 # Dask operations are read-only since graph-backed. 211 y = x 212 elif ndi == N.NUMPY: 213 y = x.view() 214 y.flags.writeable = False 215 elif ndi == N.CUPY: 216 y = x.view() 217 # y.flags.writeable = False # https://github.com/cupy/cupy/issues/2616 218 else: 219 msg = f"read_only() not yet defined for {ndi}." 220 raise NotImplementedError(msg) 221 return y