diff --git a/dev/index.html b/dev/index.html index ce24cdca..1aee85f8 100644 --- a/dev/index.html +++ b/dev/index.html @@ -4566,7 +4566,13 @@

Source code in imops/morphology.py -
168
+              
162
+163
+164
+165
+166
+167
+168
 169
 170
 171
@@ -4599,52 +4605,46 @@ 

198 199 200 -201 -202 -203 -204 -205 -206 -207

def binary_dilation(
-    image: np.ndarray,
-    footprint: np.ndarray = None,
-    output: np.ndarray = None,
-    boxed: bool = False,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> np.ndarray:
-    """
-    Fast parallelizable binary morphological dilation of an image
-
-    Parameters
-    ----------
-    image: np.ndarray
-        input image
-    footprint: np.ndarray
-        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
-    output: np.ndarray
-        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
-        array is created
-    boxed: bool
-        if True, dilation is performed on cropped image which may speed up computation depedning on how localized True
-        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
-        exotic (has even shape or center pixel is False)
-    num_threads: int
-        the number of threads to use for computation. Default = the cpu count. If negative value passed
-        cpu count + num_threads + 1 threads will be used
-    backend: BackendLike
-        which backend to use. `cython` and `scipy` are available, `cython` is used by default
-
-    Returns
-    -------
-    dilated: np.ndarray
-        the result of morphological dilation
-
-    Examples
-    --------
-    >>> dilated = binary_dilation(x)
-    """
-    return _binary_dilation(image, footprint, output, boxed, num_threads, backend)
+201
def binary_dilation(
+    image: np.ndarray,
+    footprint: np.ndarray = None,
+    output: np.ndarray = None,
+    boxed: bool = False,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> np.ndarray:
+    """
+    Fast parallelizable binary morphological dilation of an image
+
+    Parameters
+    ----------
+    image: np.ndarray
+        input image
+    footprint: np.ndarray
+        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
+    output: np.ndarray
+        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
+        array is created
+    boxed: bool
+        if True, dilation is performed on cropped image which may speed up computation depedning on how localized True
+        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
+        exotic (has even shape or center pixel is False)
+    num_threads: int
+        the number of threads to use for computation. Default = the cpu count. If negative value passed
+        cpu count + num_threads + 1 threads will be used
+    backend: BackendLike
+        which backend to use. `cython` and `scipy` are available, `cython` is used by default
+
+    Returns
+    -------
+    dilated: np.ndarray
+        the result of morphological dilation
+
+    Examples
+    --------
+    >>> dilated = binary_dilation(x)
+    """
+    return _binary_dilation(image, footprint, output, boxed, num_threads, backend)
 
@@ -4797,7 +4797,13 @@

Source code in imops/morphology.py -
220
+              
214
+215
+216
+217
+218
+219
+220
 221
 222
 223
@@ -4830,52 +4836,46 @@ 

250 251 252 -253 -254 -255 -256 -257 -258 -259

def binary_erosion(
-    image: np.ndarray,
-    footprint: np.ndarray = None,
-    output: np.ndarray = None,
-    boxed: bool = False,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> np.ndarray:
-    """
-    Fast parallelizable binary morphological erosion of an image
-
-    Parameters
-    ----------
-    image: np.ndarray
-        input image
-    footprint: np.ndarray
-        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
-    output: np.ndarray
-        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
-        array is created
-    boxed: bool
-        if True, erosion is performed on cropped image which may speed up computation depedning on how localized True
-        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
-        exotic (has even shape or center pixel is False)
-    num_threads: int
-        the number of threads to use for computation. Default = the cpu count. If negative value passed
-        cpu count + num_threads + 1 threads will be used
-    backend: BackendLike
-        which backend to use. `cython` and `scipy` are available, `cython` is used by default
-
-    Returns
-    -------
-    eroded: np.ndarray
-        the result of morphological erosion
-
-    Examples
-    --------
-    >>> eroded = binary_erosion(x)
-    """
-    return _binary_erosion(image, footprint, output, boxed, num_threads, backend)
+253
def binary_erosion(
+    image: np.ndarray,
+    footprint: np.ndarray = None,
+    output: np.ndarray = None,
+    boxed: bool = False,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> np.ndarray:
+    """
+    Fast parallelizable binary morphological erosion of an image
+
+    Parameters
+    ----------
+    image: np.ndarray
+        input image
+    footprint: np.ndarray
+        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
+    output: np.ndarray
+        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
+        array is created
+    boxed: bool
+        if True, erosion is performed on cropped image which may speed up computation depedning on how localized True
+        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
+        exotic (has even shape or center pixel is False)
+    num_threads: int
+        the number of threads to use for computation. Default = the cpu count. If negative value passed
+        cpu count + num_threads + 1 threads will be used
+    backend: BackendLike
+        which backend to use. `cython` and `scipy` are available, `cython` is used by default
+
+    Returns
+    -------
+    eroded: np.ndarray
+        the result of morphological erosion
+
+    Examples
+    --------
+    >>> eroded = binary_erosion(x)
+    """
+    return _binary_erosion(image, footprint, output, boxed, num_threads, backend)
 
@@ -5028,7 +5028,13 @@

Source code in imops/morphology.py -
325
+              
319
+320
+321
+322
+323
+324
+325
 326
 327
 328
@@ -5062,53 +5068,47 @@ 

356 357 358 -359 -360 -361 -362 -363 -364 -365

def binary_opening(
-    image: np.ndarray,
-    footprint: np.ndarray = None,
-    output: np.ndarray = None,
-    boxed: bool = False,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> np.ndarray:
-    """
-    Fast parallelizable binary morphological opening of an image
-
-    Parameters
-    ----------
-    image: np.ndarray
-        input image
-    footprint: np.ndarray
-        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
-    output: np.ndarray
-        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
-        array is created
-    boxed: bool
-        if True, opening is performed on cropped image which may speed up computation depedning on how localized True
-        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
-        exotic (has even shape or center pixel is False)
-    num_threads: int
-        the number of threads to use for computation. Default = the cpu count. If negative value passed
-        cpu count + num_threads + 1 threads will be used
-    backend: BackendLike
-        which backend to use. `cython` and `scipy` are available, `cython` is used by default
-
-    Returns
-    -------
-    opened: np.ndarray
-        the result of morphological opening
-
-    Examples
-    --------
-    >>> opened = binary_opening(x)
-    """
-
-    return _binary_opening(image, footprint, output, boxed, num_threads, backend)
+359
def binary_opening(
+    image: np.ndarray,
+    footprint: np.ndarray = None,
+    output: np.ndarray = None,
+    boxed: bool = False,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> np.ndarray:
+    """
+    Fast parallelizable binary morphological opening of an image
+
+    Parameters
+    ----------
+    image: np.ndarray
+        input image
+    footprint: np.ndarray
+        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
+    output: np.ndarray
+        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
+        array is created
+    boxed: bool
+        if True, opening is performed on cropped image which may speed up computation depedning on how localized True
+        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
+        exotic (has even shape or center pixel is False)
+    num_threads: int
+        the number of threads to use for computation. Default = the cpu count. If negative value passed
+        cpu count + num_threads + 1 threads will be used
+    backend: BackendLike
+        which backend to use. `cython` and `scipy` are available, `cython` is used by default
+
+    Returns
+    -------
+    opened: np.ndarray
+        the result of morphological opening
+
+    Examples
+    --------
+    >>> opened = binary_opening(x)
+    """
+
+    return _binary_opening(image, footprint, output, boxed, num_threads, backend)
 
@@ -5261,7 +5261,13 @@

Source code in imops/morphology.py -
272
+              
266
+267
+268
+269
+270
+271
+272
 273
 274
 275
@@ -5295,53 +5301,47 @@ 

303 304 305 -306 -307 -308 -309 -310 -311 -312

def binary_closing(
-    image: np.ndarray,
-    footprint: np.ndarray = None,
-    output: np.ndarray = None,
-    boxed: bool = False,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> np.ndarray:
-    """
-    Fast parallelizable binary morphological closing of an image
-
-    Parameters
-    ----------
-    image: np.ndarray
-        input image
-    footprint: np.ndarray
-        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
-    output: np.ndarray
-        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
-        array is created
-    boxed: bool
-        if True, closing is performed on cropped image which may speed up computation depedning on how localized True
-        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
-        exotic (has even shape or center pixel is False)
-    num_threads: int
-        the number of threads to use for computation. Default = the cpu count. If negative value passed
-        cpu count + num_threads + 1 threads will be used
-    backend: BackendLike
-        which backend to use. `cython` and `scipy` are available, `cython` is used by default
-
-    Returns
-    -------
-    closed: np.ndarray
-        the result of morphological closing
-
-    Examples
-    --------
-    >>> closed = binary_closing(x)
-    """
-
-    return _binary_closing(image, footprint, output, boxed, num_threads, backend)
+306
def binary_closing(
+    image: np.ndarray,
+    footprint: np.ndarray = None,
+    output: np.ndarray = None,
+    boxed: bool = False,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> np.ndarray:
+    """
+    Fast parallelizable binary morphological closing of an image
+
+    Parameters
+    ----------
+    image: np.ndarray
+        input image
+    footprint: np.ndarray
+        the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
+    output: np.ndarray
+        array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
+        array is created
+    boxed: bool
+        if True, closing is performed on cropped image which may speed up computation depedning on how localized True
+        pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
+        exotic (has even shape or center pixel is False)
+    num_threads: int
+        the number of threads to use for computation. Default = the cpu count. If negative value passed
+        cpu count + num_threads + 1 threads will be used
+    backend: BackendLike
+        which backend to use. `cython` and `scipy` are available, `cython` is used by default
+
+    Returns
+    -------
+    closed: np.ndarray
+        the result of morphological closing
+
+    Examples
+    --------
+    >>> closed = binary_closing(x)
+    """
+
+    return _binary_closing(image, footprint, output, boxed, num_threads, backend)
 
@@ -5560,7 +5560,13 @@

Source code in imops/morphology.py -
368
+              
362
+363
+364
+365
+366
+367
+368
 369
 370
 371
@@ -5703,162 +5709,156 @@ 

508 509 510 -511 -512 -513 -514 -515 -516 -517

def distance_transform_edt(
-    image: np.ndarray,
-    sampling: Tuple[float] = None,
-    return_distances: bool = True,
-    return_indices: bool = False,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> Union[np.ndarray, Tuple[np.ndarray]]:
-    """
-    Fast parallelizable Euclidean distance transform for <= 3D inputs
-
-    This function calculates the distance transform of the `image`, by
-    replacing each foreground (non-zero) element, with its
-    shortest distance to the background (any zero-valued element).
-
-    In addition to the distance transform, the feature transform can
-    be calculated. In this case the index of the closest background
-    element to each foreground element is returned in a separate array.
-
-    Parameters
-    ----------
-    image : array_like
-        input data to transform. Can be any type but will be converted
-        into binary: 1 wherever input equates to True, 0 elsewhere
-    sampling : tuple of `image.ndim` floats, optional
-        spacing of elements along each dimension. If a sequence, must be of
-        length equal to the input rank; if a single number, this is used for
-        all axes. If not specified, a grid spacing of unity is implied
-    return_distances : bool, optional
-        whether to calculate the distance transform.
-        Default is True
-    return_indices : bool, optional
-        whether to calculate the feature transform.
-        Default is False
-    num_threads: int
-        the number of threads to use for computation. Default = the cpu count. If negative value passed
-        cpu count + num_threads + 1 threads will be used
-    backend: BackendLike
-        which backend to use. `cython` and `scipy` are available, `cython` is used by default
-
-    Returns
-    -------
-    distances : float32 ndarray, optional
-        the calculated distance transform. Returned only when
-        `return_distances` is True and `distances` is not supplied.
-        It will have the same shape as the input array
-    indices : int32 ndarray, optional
-        the calculated feature transform. It has an input-shaped array for each
-        dimension of the input. See example below.
-        Returned only when `return_indices` is True and `indices` is not
-        supplied
-
-    Notes
-    -----
-    The Euclidean distance transform gives values of the Euclidean
-    distance::
-
-                    n
-      y_i = sqrt(sum (x[i]-b[i])**2)
-                    i
-
-    where b[i] is the background point (value 0) with the smallest
-    Euclidean distance to input points x[i], and n is the
-    number of dimensions.
-
-    Examples
-    --------
-    import numpy as np
-    a = np.array(([0,1,1,1,1],
-                  [0,0,1,1,1],
-                  [0,1,1,1,1],
-                  [0,1,1,1,0],
-                  [0,1,1,0,0]))
-    distance_transform_edt(a)
-    array([[ 0.    ,  1.    ,  1.4142,  2.2361,  3.    ],
-           [ 0.    ,  0.    ,  1.    ,  2.    ,  2.    ],
-           [ 0.    ,  1.    ,  1.4142,  1.4142,  1.    ],
-           [ 0.    ,  1.    ,  1.4142,  1.    ,  0.    ],
-           [ 0.    ,  1.    ,  1.    ,  0.    ,  0.    ]])
-
-    With a sampling of 2 units along x, 1 along y:
-
-    distance_transform_edt(a, sampling=[2, 1])
-    array([[ 0.    ,  1.    ,  2.    ,  2.8284,  3.6056],
-           [ 0.    ,  0.    ,  1.    ,  2.    ,  3.    ],
-           [ 0.    ,  1.    ,  2.    ,  2.2361,  2.    ],
-           [ 0.    ,  1.    ,  2.    ,  1.    ,  0.    ],
-           [ 0.    ,  1.    ,  1.    ,  0.    ,  0.    ]])
-
-    Asking for indices as well:
-
-    edt, inds = distance_transform_edt(a, return_indices=True)
-    inds
-    array([[[0, 0, 1, 1, 3],
-            [1, 1, 1, 1, 3],
-            [2, 2, 1, 3, 3],
-            [3, 3, 4, 4, 3],
-            [4, 4, 4, 4, 4]],
-           [[0, 0, 1, 1, 4],
-            [0, 1, 1, 1, 4],
-            [0, 0, 1, 4, 4],
-            [0, 0, 3, 3, 4],
-            [0, 0, 3, 3, 4]]])
-    """
-    backend = resolve_backend(backend, warn_stacklevel=3)
-    if backend.name not in ('Scipy', 'Cython'):
-        raise ValueError(f'Unsupported backend "{backend.name}".')
-
-    num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
-
-    if backend.name == 'Scipy':
-        return scipy_distance_transform_edt(image, sampling, return_distances, return_indices)
-
-    if image.ndim > 3:
-        warn("Fast Euclidean Distance Transform is only supported for ndim<=3. Falling back to scipy's implementation.")
-        return scipy_distance_transform_edt(image, sampling, return_distances, return_indices)
-
-    if (not return_distances) and (not return_indices):
-        raise RuntimeError('At least one of `return_distances`/`return_indices` must be True')
-    if image.dtype != bool:
-        image = np.atleast_1d(np.where(image, 1, 0))
-    if sampling is not None:
-        sampling = _ni_support._normalize_sequence(sampling, image.ndim)
-        sampling = np.asarray(sampling, dtype=np.float64)
-        if not sampling.flags.contiguous:
-            sampling = sampling.copy()
-
-    if return_indices:
-        ft = np.zeros((image.ndim,) + image.shape, dtype=np.int32)
-        euclidean_feature_transform(image, sampling, ft)
+511
def distance_transform_edt(
+    image: np.ndarray,
+    sampling: Tuple[float] = None,
+    return_distances: bool = True,
+    return_indices: bool = False,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> Union[np.ndarray, Tuple[np.ndarray]]:
+    """
+    Fast parallelizable Euclidean distance transform for <= 3D inputs
+
+    This function calculates the distance transform of the `image`, by
+    replacing each foreground (non-zero) element, with its
+    shortest distance to the background (any zero-valued element).
+
+    In addition to the distance transform, the feature transform can
+    be calculated. In this case the index of the closest background
+    element to each foreground element is returned in a separate array.
+
+    Parameters
+    ----------
+    image : array_like
+        input data to transform. Can be any type but will be converted
+        into binary: 1 wherever input equates to True, 0 elsewhere
+    sampling : tuple of `image.ndim` floats, optional
+        spacing of elements along each dimension. If a sequence, must be of
+        length equal to the input rank; if a single number, this is used for
+        all axes. If not specified, a grid spacing of unity is implied
+    return_distances : bool, optional
+        whether to calculate the distance transform.
+        Default is True
+    return_indices : bool, optional
+        whether to calculate the feature transform.
+        Default is False
+    num_threads: int
+        the number of threads to use for computation. Default = the cpu count. If negative value passed
+        cpu count + num_threads + 1 threads will be used
+    backend: BackendLike
+        which backend to use. `cython` and `scipy` are available, `cython` is used by default
+
+    Returns
+    -------
+    distances : float32 ndarray, optional
+        the calculated distance transform. Returned only when
+        `return_distances` is True and `distances` is not supplied.
+        It will have the same shape as the input array
+    indices : int32 ndarray, optional
+        the calculated feature transform. It has an input-shaped array for each
+        dimension of the input. See example below.
+        Returned only when `return_indices` is True and `indices` is not
+        supplied
+
+    Notes
+    -----
+    The Euclidean distance transform gives values of the Euclidean
+    distance::
+
+                    n
+      y_i = sqrt(sum (x[i]-b[i])**2)
+                    i
+
+    where b[i] is the background point (value 0) with the smallest
+    Euclidean distance to input points x[i], and n is the
+    number of dimensions.
+
+    Examples
+    --------
+    import numpy as np
+    a = np.array(([0,1,1,1,1],
+                  [0,0,1,1,1],
+                  [0,1,1,1,1],
+                  [0,1,1,1,0],
+                  [0,1,1,0,0]))
+    distance_transform_edt(a)
+    array([[ 0.    ,  1.    ,  1.4142,  2.2361,  3.    ],
+           [ 0.    ,  0.    ,  1.    ,  2.    ,  2.    ],
+           [ 0.    ,  1.    ,  1.4142,  1.4142,  1.    ],
+           [ 0.    ,  1.    ,  1.4142,  1.    ,  0.    ],
+           [ 0.    ,  1.    ,  1.    ,  0.    ,  0.    ]])
+
+    With a sampling of 2 units along x, 1 along y:
+
+    distance_transform_edt(a, sampling=[2, 1])
+    array([[ 0.    ,  1.    ,  2.    ,  2.8284,  3.6056],
+           [ 0.    ,  0.    ,  1.    ,  2.    ,  3.    ],
+           [ 0.    ,  1.    ,  2.    ,  2.2361,  2.    ],
+           [ 0.    ,  1.    ,  2.    ,  1.    ,  0.    ],
+           [ 0.    ,  1.    ,  1.    ,  0.    ,  0.    ]])
+
+    Asking for indices as well:
+
+    edt, inds = distance_transform_edt(a, return_indices=True)
+    inds
+    array([[[0, 0, 1, 1, 3],
+            [1, 1, 1, 1, 3],
+            [2, 2, 1, 3, 3],
+            [3, 3, 4, 4, 3],
+            [4, 4, 4, 4, 4]],
+           [[0, 0, 1, 1, 4],
+            [0, 1, 1, 1, 4],
+            [0, 0, 1, 4, 4],
+            [0, 0, 3, 3, 4],
+            [0, 0, 3, 3, 4]]])
+    """
+    backend = resolve_backend(backend, warn_stacklevel=3)
+    if backend.name not in ('Scipy', 'Cython'):
+        raise ValueError(f'Unsupported backend "{backend.name}".')
+
+    num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
+
+    if backend.name == 'Scipy':
+        return scipy_distance_transform_edt(image, sampling, return_distances, return_indices)
+
+    if image.ndim > 3:
+        warn("Fast Euclidean Distance Transform is only supported for ndim<=3. Falling back to scipy's implementation.")
+        return scipy_distance_transform_edt(image, sampling, return_distances, return_indices)
+
+    if (not return_distances) and (not return_indices):
+        raise RuntimeError('At least one of `return_distances`/`return_indices` must be True')
+    if image.dtype != bool:
+        image = np.atleast_1d(np.where(image, 1, 0))
+    if sampling is not None:
+        sampling = _ni_support._normalize_sequence(sampling, image.ndim)
+        sampling = np.asarray(sampling, dtype=np.float64)
+        if not sampling.flags.contiguous:
+            sampling = sampling.copy()
+
+    if return_indices:
+        ft = np.zeros((image.ndim,) + image.shape, dtype=np.int32)
+        euclidean_feature_transform(image, sampling, ft)
+
+    if return_distances:
+        if sampling is not None:
+            dt = edt(image, anisotropy=sampling.astype(np.float32), parallel=num_threads)
+        else:
+            dt = edt(image, parallel=num_threads)
 
-    if return_distances:
-        if sampling is not None:
-            dt = edt(image, anisotropy=sampling.astype(np.float32), parallel=num_threads)
-        else:
-            dt = edt(image, parallel=num_threads)
+    result = []
+    if return_distances:
+        result.append(dt)
+    if return_indices:
+        result.append(ft)
 
-    result = []
-    if return_distances:
-        result.append(dt)
-    if return_indices:
-        result.append(ft)
+    if len(result) == 2:
+        return tuple(result)
+
+    if len(result) == 1:
+        return result[0]
 
-    if len(result) == 2:
-        return tuple(result)
-
-    if len(result) == 1:
-        return result[0]
-
-    return None
+    return None
 
@@ -7784,7 +7784,12 @@

Source code in imops/radon.py -
 21
+              
 16
+ 17
+ 18
+ 19
+ 20
+ 21
  22
  23
  24
@@ -7863,96 +7868,91 @@ 

97 98 99 -100 -101 -102 -103 -104 -105

def radon(
-    image: np.ndarray,
-    axes: Tuple[int, int] = None,
-    theta: Union[int, Sequence[float]] = 180,
-    return_fill: bool = False,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> Union[np.ndarray, Tuple[np.ndarray, float]]:
-    """
-    Fast implementation of Radon transform. Adapted from scikit-image.
-
-    Parameters
-    ----------
-    image: np.ndarray
-        an n-dimensional array with at least 2 axes
-    axes: tuple[int, int]
-        the axes in the `image` along which the Radon transform will be applied.
-        The `image` shape along the `axes` must be of the same length
-    theta: int | Sequence[float]
-        the angles for which the Radon transform will be computed. If it is an integer - the angles will
-        be evenly distributed between 0 and 180, `theta` values in total
-    return_fill: bool
-        whether to return the value that fills the image outside the circle working area
-    num_threads: int
-        the number of threads to be used for parallel computation. By default - equals to the number of cpu cores
-    backend: str | Backend
-        the execution backend. Currently only "Cython" is avaliable
-
-    Returns
-    -------
-    sinogram: np.ndarray
-        the result of the Radon transform
-    fill_value: float
-        the value that fills the image outside the circle working area. Returned only if `return_fill` is True
-
-    Examples
-    --------
-    >>> sinogram = radon(image)  # 2d image
-    >>> sinogram, fill_value = radon(image, return_fill=True)  # 2d image with fill value
-    >>> sinogram = radon(image, axes=(-2, -1))  # nd image
-    """
-    backend = resolve_backend(backend, warn_stacklevel=3)
-    if backend.name not in ('Cython',):
-        raise ValueError(f'Unsupported backend "{backend.name}".')
-
-    image, axes, extra = normalize_axes(image, axes)
-    if image.shape[1] != image.shape[2]:
-        raise ValueError(
-            f'The image must be square along the provided axes ({axes}), but has shape: {image.shape[1:]}.'
-        )
-
-    if isinstance(theta, int):
-        theta = np.linspace(0, 180, theta, endpoint=False)
-
-    size = image.shape[1]
-    radius = size // 2
-    xs = np.arange(-radius, size - radius)
-    squared = xs**2
-    outside_circle = (squared[:, None] + squared[None, :]) > radius**2
-    values = image[:, outside_circle]
-    min_, max_ = values.min(), values.max()
-    if max_ - min_ > 0.1:
-        raise ValueError(
-            f'The image must be constant outside the circle. ' f'Got values ranging from {min_} to {max_}.'
-        )
+100
def radon(
+    image: np.ndarray,
+    axes: Tuple[int, int] = None,
+    theta: Union[int, Sequence[float]] = 180,
+    return_fill: bool = False,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> Union[np.ndarray, Tuple[np.ndarray, float]]:
+    """
+    Fast implementation of Radon transform. Adapted from scikit-image.
+
+    Parameters
+    ----------
+    image: np.ndarray
+        an n-dimensional array with at least 2 axes
+    axes: tuple[int, int]
+        the axes in the `image` along which the Radon transform will be applied.
+        The `image` shape along the `axes` must be of the same length
+    theta: int | Sequence[float]
+        the angles for which the Radon transform will be computed. If it is an integer - the angles will
+        be evenly distributed between 0 and 180, `theta` values in total
+    return_fill: bool
+        whether to return the value that fills the image outside the circle working area
+    num_threads: int
+        the number of threads to be used for parallel computation. By default - equals to the number of cpu cores
+    backend: str | Backend
+        the execution backend. Currently only "Cython" is avaliable
+
+    Returns
+    -------
+    sinogram: np.ndarray
+        the result of the Radon transform
+    fill_value: float
+        the value that fills the image outside the circle working area. Returned only if `return_fill` is True
+
+    Examples
+    --------
+    >>> sinogram = radon(image)  # 2d image
+    >>> sinogram, fill_value = radon(image, return_fill=True)  # 2d image with fill value
+    >>> sinogram = radon(image, axes=(-2, -1))  # nd image
+    """
+    backend = resolve_backend(backend, warn_stacklevel=3)
+    if backend.name not in ('Cython',):
+        raise ValueError(f'Unsupported backend "{backend.name}".')
+
+    image, axes, extra = normalize_axes(image, axes)
+    if image.shape[1] != image.shape[2]:
+        raise ValueError(
+            f'The image must be square along the provided axes ({axes}), but has shape: {image.shape[1:]}.'
+        )
+
+    if isinstance(theta, int):
+        theta = np.linspace(0, 180, theta, endpoint=False)
+
+    size = image.shape[1]
+    radius = size // 2
+    xs = np.arange(-radius, size - radius)
+    squared = xs**2
+    outside_circle = (squared[:, None] + squared[None, :]) > radius**2
+    values = image[:, outside_circle]
+    min_, max_ = values.min(), values.max()
+    if max_ - min_ > 0.1:
+        raise ValueError(
+            f'The image must be constant outside the circle. ' f'Got values ranging from {min_} to {max_}.'
+        )
+
+    if min_ != 0 or max_ != 0:
+        # FIXME: how to accurately pass `num_threads` and `backend` arguments to `copy`?
+        image = copy(image, order='C')
+        image[:, outside_circle] = 0
 
-    if min_ != 0 or max_ != 0:
-        # FIXME: how to accurately pass `num_threads` and `backend` arguments to `copy`?
-        image = copy(image, order='C')
-        image[:, outside_circle] = 0
+    # TODO: f(arange)?
+    limits = ((squared[:, None] + squared[None, :]) > (radius + 2) ** 2).sum(0) // 2
+
+    num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
 
-    # TODO: f(arange)?
-    limits = ((squared[:, None] + squared[None, :]) > (radius + 2) ** 2).sum(0) // 2
-
-    num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
-
-    radon3d_ = fast_radon3d if backend.fast else radon3d
-
-    sinogram = radon3d_(image, np.deg2rad(theta, dtype=image.dtype), limits, num_threads)
-
-    result = restore_axes(sinogram, axes, extra)
-    if return_fill:
-        result = result, min_
-
-    return result
+    radon3d_ = fast_radon3d if backend.fast else radon3d
+
+    sinogram = radon3d_(image, np.deg2rad(theta, dtype=image.dtype), limits, num_threads)
+
+    result = restore_axes(sinogram, axes, extra)
+    if return_fill:
+        result = result, min_
+
+    return result
 
@@ -8132,7 +8132,12 @@

Source code in imops/radon.py -
108
+              
103
+104
+105
+106
+107
+108
 109
 110
 111
@@ -8221,106 +8226,101 @@ 

194 195 196 -197 -198 -199 -200 -201 -202

def inverse_radon(
-    sinogram: np.ndarray,
-    axes: Tuple[int, int] = None,
-    theta: Union[int, Sequence[float]] = None,
-    fill_value: float = 0,
-    a: float = 0,
-    b: float = 1,
-    num_threads: int = -1,
-    backend: BackendLike = None,
-) -> np.ndarray:
-    """
-    Fast implementation of inverse Radon transform. Adapted from scikit-image.
-
-    Parameters
-    ----------
-    sinogram: np.ndarray
-        an n-dimensional array with at least 2 axes
-    axes: tuple[int, int]
-        the axes in the `image` along which the inverse Radon transform will be applied
-    theta: int | Sequence[float]
-        the angles for which the inverse Radon transform will be computed. If it is an integer - the angles will
-        be evenly distributed between 0 and 180, `theta` values in total
-    fill_value: float
-        the value that fills the image outside the circle working area. Can be returned by `radon`
-    a: float
-        the first parameter of the sharpen filter
-    b: float
-        the second parameter of the sharpen filter
-    num_threads: int
-        the number of threads to be used for parallel computation. By default - equals to the number of cpu cores
-    backend: str | Backend
-        the execution backend. Currently only "Cython" is avaliable
+197
def inverse_radon(
+    sinogram: np.ndarray,
+    axes: Tuple[int, int] = None,
+    theta: Union[int, Sequence[float]] = None,
+    fill_value: float = 0,
+    a: float = 0,
+    b: float = 1,
+    num_threads: int = -1,
+    backend: BackendLike = None,
+) -> np.ndarray:
+    """
+    Fast implementation of inverse Radon transform. Adapted from scikit-image.
+
+    Parameters
+    ----------
+    sinogram: np.ndarray
+        an n-dimensional array with at least 2 axes
+    axes: tuple[int, int]
+        the axes in the `image` along which the inverse Radon transform will be applied
+    theta: int | Sequence[float]
+        the angles for which the inverse Radon transform will be computed. If it is an integer - the angles will
+        be evenly distributed between 0 and 180, `theta` values in total
+    fill_value: float
+        the value that fills the image outside the circle working area. Can be returned by `radon`
+    a: float
+        the first parameter of the sharpen filter
+    b: float
+        the second parameter of the sharpen filter
+    num_threads: int
+        the number of threads to be used for parallel computation. By default - equals to the number of cpu cores
+    backend: str | Backend
+        the execution backend. Currently only "Cython" is avaliable
+
+    Returns
+    -------
+    image: np.ndarray
+        the result of the inverse Radon transform
 
-    Returns
-    -------
-    image: np.ndarray
-        the result of the inverse Radon transform
-
-    Examples
-    --------
-    >>> image = inverse_radon(sinogram)  # 2d image
-    >>> image = inverse_radon(sinogram, fill_value=-1000)  # 2d image with fill value
-    >>> image = inverse_radon(sinogram, axes=(-2, -1))  # nd image
-    """
-    backend = resolve_backend(backend, warn_stacklevel=3)
-    if backend.name not in ('Cython',):
-        raise ValueError(f'Unsupported backend "{backend.name}".')
-
-    sinogram, axes, extra = normalize_axes(sinogram, axes)
+    Examples
+    --------
+    >>> image = inverse_radon(sinogram)  # 2d image
+    >>> image = inverse_radon(sinogram, fill_value=-1000)  # 2d image with fill value
+    >>> image = inverse_radon(sinogram, axes=(-2, -1))  # nd image
+    """
+    backend = resolve_backend(backend, warn_stacklevel=3)
+    if backend.name not in ('Cython',):
+        raise ValueError(f'Unsupported backend "{backend.name}".')
+
+    sinogram, axes, extra = normalize_axes(sinogram, axes)
+
+    if theta is None:
+        theta = sinogram.shape[-1]
+    if isinstance(theta, int):
+        theta = np.linspace(0, 180, theta, endpoint=False)
 
-    if theta is None:
-        theta = sinogram.shape[-1]
-    if isinstance(theta, int):
-        theta = np.linspace(0, 180, theta, endpoint=False)
-
-    angles_count = len(theta)
-    if angles_count != sinogram.shape[-1]:
-        raise ValueError(
-            f'The given `theta` (size {angles_count}) does not match the number of '
-            f'projections in `sinogram` ({sinogram.shape[-1]}).'
-        )
-    output_size = sinogram.shape[1]
-    sinogram = _sinogram_circle_to_square(sinogram)
-
-    img_shape = sinogram.shape[1]
-    # Resize image to next power of two (but no less than 64) for
-    # Fourier analysis; speeds up Fourier and lessens artifacts
-    # TODO: why *2?
-    projection_size_padded = max(64, int(2 ** np.ceil(np.log2(2 * img_shape))))
-    pad_width = ((0, 0), (0, projection_size_padded - img_shape), (0, 0))
-    padded_sinogram = np.pad(sinogram, pad_width, mode='constant', constant_values=0)
-    fourier_filter = _smooth_sharpen_filter(projection_size_padded, a, b)
-
-    # Apply filter in Fourier domain
-    fourier_img = fft(padded_sinogram, axis=1) * fourier_filter
-    filtered_sinogram = np.real(ifft(fourier_img, axis=1)[:, :img_shape, :])
+    angles_count = len(theta)
+    if angles_count != sinogram.shape[-1]:
+        raise ValueError(
+            f'The given `theta` (size {angles_count}) does not match the number of '
+            f'projections in `sinogram` ({sinogram.shape[-1]}).'
+        )
+    output_size = sinogram.shape[1]
+    sinogram = _sinogram_circle_to_square(sinogram)
+
+    img_shape = sinogram.shape[1]
+    # Resize image to next power of two (but no less than 64) for
+    # Fourier analysis; speeds up Fourier and lessens artifacts
+    # TODO: why *2?
+    projection_size_padded = max(64, int(2 ** np.ceil(np.log2(2 * img_shape))))
+    pad_width = ((0, 0), (0, projection_size_padded - img_shape), (0, 0))
+    padded_sinogram = np.pad(sinogram, pad_width, mode='constant', constant_values=0)
+    fourier_filter = _smooth_sharpen_filter(projection_size_padded, a, b)
+
+    # Apply filter in Fourier domain
+    fourier_img = fft(padded_sinogram, axis=1) * fourier_filter
+    filtered_sinogram = np.real(ifft(fourier_img, axis=1)[:, :img_shape, :])
+
+    radius = output_size // 2
+    xs = np.arange(-radius, output_size - radius)
+    squared = xs**2
+    inside_circle = (squared[:, None] + squared[None, :]) <= radius**2
 
-    radius = output_size // 2
-    xs = np.arange(-radius, output_size - radius)
-    squared = xs**2
-    inside_circle = (squared[:, None] + squared[None, :]) <= radius**2
-
-    dtype = sinogram.dtype
-    filtered_sinogram = filtered_sinogram.astype(dtype, copy=False)
-    theta, xs = np.deg2rad(theta, dtype=dtype), xs.astype(dtype, copy=False)
-
-    num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
-
-    backprojection3d_ = fast_backprojection3d if backend.fast else backprojection3d
-
-    reconstructed = np.asarray(
-        backprojection3d_(filtered_sinogram, theta, xs, inside_circle, fill_value, img_shape, output_size, num_threads)
-    )
-
-    return restore_axes(reconstructed, axes, extra)
+    dtype = sinogram.dtype
+    filtered_sinogram = filtered_sinogram.astype(dtype, copy=False)
+    theta, xs = np.deg2rad(theta, dtype=dtype), xs.astype(dtype, copy=False)
+
+    num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
+
+    backprojection3d_ = fast_backprojection3d if backend.fast else backprojection3d
+
+    reconstructed = np.asarray(
+        backprojection3d_(filtered_sinogram, theta, xs, inside_circle, fill_value, img_shape, output_size, num_threads)
+    )
+
+    return restore_axes(reconstructed, axes, extra)
 
@@ -8431,7 +8431,12 @@

Source code in imops/utils.py -
213
+              
208
+209
+210
+211
+212
+213
 214
 215
 216
@@ -8464,50 +8469,45 @@ 

243 244 245 -246 -247 -248 -249 -250 -251

def isin(element: np.ndarray, test_elements: np.ndarray, num_threads: int = 1) -> np.ndarray:
-    """
-    Calculates `element in test_elements`, broadcasting over `element` only.
-    Returns a boolean array of the same shape as `element` that is True where
-    an element of `element` is in `test_elements` and False otherwise.
-
-    Parameters
-    ----------
-    element: np.ndarray
-        n-dimensional array
-    test_elements: np.ndarray
-        1-d array of the values against which to test each value of element
-    num_threads: int
-        the number of threads to use for computation. Default = 1. If negative value passed
-        cpu count + num_threads + 1 threads will be used
+246
def isin(element: np.ndarray, test_elements: np.ndarray, num_threads: int = 1) -> np.ndarray:
+    """
+    Calculates `element in test_elements`, broadcasting over `element` only.
+    Returns a boolean array of the same shape as `element` that is True where
+    an element of `element` is in `test_elements` and False otherwise.
+
+    Parameters
+    ----------
+    element: np.ndarray
+        n-dimensional array
+    test_elements: np.ndarray
+        1-d array of the values against which to test each value of element
+    num_threads: int
+        the number of threads to use for computation. Default = 1. If negative value passed
+        cpu count + num_threads + 1 threads will be used
+
+    Returns
+    -------
+    isin: np.ndarray, bool
+        has the same shape as `element`. The values `element[isin]` are in `test_elements`
 
-    Returns
-    -------
-    isin: np.ndarray, bool
-        has the same shape as `element`. The values `element[isin]` are in `test_elements`
-
-    Examples
-    --------
-    element = 2*np.arange(4).reshape((2, 2))
-    test_elements = [1, 2, 4, 8]
-    mask = isin(element, test_elements)
-    """
-    if element.dtype not in ('int16', 'int32', 'int64'):
-        raise ValueError(f'Supported dtypes: int16, int32, int64, got {element.dtype}')
-
-    num_threads = normalize_num_threads(num_threads, Cython(), warn_stacklevel=2)
-
-    contiguos_element = np.ascontiguousarray(element)
-    test_elements = np.asarray(test_elements, dtype=element.dtype)
-    out = np.zeros_like(contiguos_element, dtype=bool)
-
-    cython_isin(contiguos_element.ravel(), test_elements, out.ravel(), num_threads)
-
-    return out
+    Examples
+    --------
+    element = 2*np.arange(4).reshape((2, 2))
+    test_elements = [1, 2, 4, 8]
+    mask = isin(element, test_elements)
+    """
+    if element.dtype not in ('int16', 'int32', 'int64'):
+        raise ValueError(f'Supported dtypes: int16, int32, int64, got {element.dtype}')
+
+    num_threads = normalize_num_threads(num_threads, Cython(), warn_stacklevel=2)
+
+    contiguos_element = np.ascontiguousarray(element)
+    test_elements = np.asarray(test_elements, dtype=element.dtype)
+    out = np.zeros_like(contiguos_element, dtype=bool)
+
+    cython_isin(contiguos_element.ravel(), test_elements, out.ravel(), num_threads)
+
+    return out