Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 33 additions & 28 deletions httomolibgpu/misc/corr.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,25 @@
# ---------------------------------------------------------------------------
"""Module for data correction. For more detailed information see :ref:`data_correction_module`."""

import numpy as np
from typing import Union

from httomolibgpu import cupywrapper

cp = cupywrapper.cp
cupy_run = cupywrapper.cupy_run

from numpy import float32
from unittest.mock import Mock

if cupy_run:
from httomolibgpu.cuda_kernels import load_cuda_module
else:
load_cuda_module = Mock()
from typing import Union

from httomolibgpu.misc.utils import (
__check_variable_type,
__check_if_data_3D_array,
__check_if_data_correct_type,
__check_if_positive_nonzero,
)

__all__ = [
"median_filter",
Expand All @@ -45,7 +49,7 @@
def median_filter(
data: cp.ndarray,
kernel_size: int = 3,
dif: float = 0.0,
dif: Union[float, int] = 0.0,
) -> cp.ndarray:
"""
Applies 3D median filter to a 3D CuPy array. For more detailed information, see :ref:`method_median_filter`.
Expand All @@ -54,9 +58,9 @@ def median_filter(
----------
data : cp.ndarray
Input CuPy 3D array either float32 or uint16 data type.
kernel_size : int, optional
The size of the filter's kernel (a diameter).
dif : float, optional
kernel_size : int
The size of the filter's kernel (a "diameter").
dif : float, int
Expected difference value between outlier value and the
median value of the array, leave equal to 0 for classical median.

Expand All @@ -70,20 +74,20 @@ def median_filter(
ValueError
If the input array is not three dimensional.
"""
input_type = data.dtype
if input_type not in ["float32", "uint16"]:
raise ValueError("The input data should be either float32 or uint16 data type")

if data.ndim == 3:
if 0 in data.shape:
raise ValueError("The length of one of dimensions is equal to zero")
else:
raise ValueError("The input array must be a 3D array")

if kernel_size not in [3, 5, 7, 9, 11, 13]:
raise ValueError("Please select a correct kernel size: 3, 5, 7, 9, 11, 13")
### Data and parameters checks ###
methods_name = "median_filter/remove_outlier"
__check_if_data_3D_array(data, methods_name)
__check_if_data_correct_type(
data, accepted_type=["float32", "uint16"], methods_name=methods_name
)
__check_variable_type(
kernel_size, [int], "kernel_size", [3, 5, 7, 9, 11, 13], methods_name
)
__check_variable_type(dif, [int, float], "dif", [], methods_name)
###################################

dz, dy, dx = data.shape
input_type = data.dtype
output = cp.copy(data, order="C")

# 3d median or dezinger
Expand All @@ -108,7 +112,7 @@ def median_filter(


def remove_outlier(
data: cp.ndarray, kernel_size: int = 3, dif: float = 1000
data: cp.ndarray, kernel_size: int = 3, dif: Union[float, int] = 1000
) -> cp.ndarray:
"""Selectively applies 3D median filter to a 3D CuPy array to remove outliers. Also called a dezinger.
For more detailed information, see :ref:`method_outlier_removal`.
Expand All @@ -117,9 +121,9 @@ def remove_outlier(
----------
data : cp.ndarray
Input CuPy 3D array either float32 or uint16 data type.
kernel_size : int, optional
The size of the filter's kernel (a diameter).
dif : float, optional
kernel_size : int
The size of the filter's kernel (a "diameter").
dif : float, int
Expected difference value between the outlier value (central voxel) and the
median value of the neighbourhood. Lower values lead to median filtering.

Expand All @@ -133,8 +137,9 @@ def remove_outlier(
ValueError
Threshold value (dif) must be positive and nonzero.
"""

if dif <= 0.0:
raise ValueError("Threshold value (dif) must be positive and nonzero.")

### Data and parameters checks ###
__check_if_positive_nonzero(
dif, "dif", positive=True, nonzero=True, methods_name="remove_outlier"
)
###################################
return median_filter(data, kernel_size, dif)
61 changes: 53 additions & 8 deletions httomolibgpu/misc/denoise.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
# ---------------------------------------------------------------------------
"""Module for data denoising. For more detailed information see :ref:`data_denoising_module`."""

import numpy as np

from httomolibgpu import cupywrapper

cp = cupywrapper.cp
Expand All @@ -35,6 +33,13 @@
ROF_TV = Mock()
PD_TV = Mock()

from typing import Union

from httomolibgpu.misc.utils import (
__check_variable_type,
__check_if_data_3D_array,
__check_if_data_correct_type,
)

__all__ = [
"total_variation_ROF",
Expand All @@ -44,7 +49,7 @@

def total_variation_ROF(
data: cp.ndarray,
regularisation_parameter: float = 1e-05,
regularisation_parameter: Union[float, int] = 1e-05,
iterations: int = 3000,
time_marching_parameter: float = 0.001,
gpu_id: int = 0,
Expand All @@ -55,7 +60,6 @@ def total_variation_ROF(
This is a gradient-based algorithm for a smoothed TV term which requires a small time marching parameter and a significant number of iterations.
See more in :ref:`method_total_variation_ROF`.


Parameters
----------
data : cp.ndarray
Expand All @@ -81,6 +85,26 @@ def total_variation_ROF(
ValueError
If the input array is not float32 data type.
"""
### Data and parameters checks ###
methods_name = "total_variation_ROF"
__check_if_data_3D_array(data, methods_name)
__check_if_data_correct_type(
data, accepted_type=["float32"], methods_name=methods_name
)
__check_variable_type(
regularisation_parameter,
[float, int],
"regularisation_parameter",
[],
methods_name,
)
__check_variable_type(iterations, [int], "iterations", [], methods_name)
__check_variable_type(
time_marching_parameter, [float], "time_marching_parameter", [], methods_name
)
__check_variable_type(gpu_id, [int], "gpu_id", [], methods_name)
__check_variable_type(half_precision, [bool], "half_precision", [], methods_name)
###################################

return ROF_TV_cupy(
data,
Expand All @@ -94,11 +118,11 @@ def total_variation_ROF(

def total_variation_PD(
data: cp.ndarray,
regularisation_parameter: float = 1e-05,
regularisation_parameter: Union[float, int] = 1e-05,
iterations: int = 1000,
isotropic: bool = True,
nonnegativity: bool = False,
lipschitz_const: float = 8.0,
lipschitz_const: Union[float, int] = 8.0,
gpu_id: int = 0,
half_precision: bool = False,
) -> cp.ndarray:
Expand All @@ -109,15 +133,15 @@ def total_variation_PD(
----------
data : cp.ndarray
Input CuPy 3D array of float32 data type.
regularisation_parameter : float
regularisation_parameter : float, int
Regularisation parameter to control the level of smoothing. Defaults to 1e-05.
iterations : int
The number of iterations. Defaults to 1000.
isotropic : bool
Choose between isotropic or anisotropic TV norm. Defaults to isotropic.
nonnegativity : bool
Enable non-negativity in iterations. Defaults to False.
lipschitz_const : float
lipschitz_const : float,int
Lipschitz constant to control convergence. Defaults to 8.
gpu_id : int
GPU device index to perform processing on. Defaults to 0.
Expand All @@ -135,6 +159,27 @@ def total_variation_PD(
If the input array is not float32 data type.
"""

### Data and parameters checks ###
methods_name = "total_variation_PD"
__check_if_data_3D_array(data, methods_name)
__check_if_data_correct_type(
data, accepted_type=["float32"], methods_name=methods_name
)
__check_variable_type(
regularisation_parameter,
[float, int],
"regularisation_parameter",
[],
methods_name,
)
__check_variable_type(iterations, [int], "iterations", [], methods_name)
__check_variable_type(isotropic, [bool], "isotropic", [], methods_name)
__check_variable_type(nonnegativity, [bool], "nonnegativity", [], methods_name)
__check_variable_type(lipschitz_const, [float], "lipschitz_const", [], methods_name)
__check_variable_type(gpu_id, [int], "gpu_id", [], methods_name)
__check_variable_type(half_precision, [bool], "half_precision", [], methods_name)
###################################

methodTV = 0
if not isotropic:
methodTV = 1
Expand Down
73 changes: 53 additions & 20 deletions httomolibgpu/misc/morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@
else:
interpn = Mock()

from typing import Literal
from typing import Literal, Union

from httomolibgpu.misc.utils import (
__check_variable_type,
__check_if_data_3D_array,
__check_if_data_correct_type,
__check_if_positive_nonzero,
)

__all__ = [
"sino_360_to_180",
Expand All @@ -42,7 +49,9 @@


def sino_360_to_180(
data: cp.ndarray, overlap: float = 0, side: Literal["left", "right"] = "left"
data: cp.ndarray,
overlap: Union[float, int] = 1,
side: Literal["left", "right"] = "left",
) -> cp.ndarray:
"""
Converts 0-360 degrees sinogram to a 0-180 sinogram.
Expand All @@ -53,7 +62,7 @@ def sino_360_to_180(
----------
data : cp.ndarray
Input 3D data.
overlap : float
overlap : float,int
Overlapping number of pixels. Floats will be converted to integers.
side : string
'left' if rotation center is close to the left of the
Expand All @@ -63,26 +72,28 @@ def sino_360_to_180(
cp.ndarray
Output 3D data.
"""
if data.ndim != 3:
raise ValueError("only 3D data is supported")

### Data and parameters checks ###
methods_name = "sino_360_to_180"
__check_if_data_3D_array(data, methods_name)
__check_if_data_correct_type(
data, accepted_type=["float32", "uint16"], methods_name=methods_name
)
__check_variable_type(overlap, [int, float], "overlap", [], methods_name)
__check_variable_type(side, [str], "side", ["left", "right"], methods_name)
__check_if_positive_nonzero(
overlap, "overlap", positive=True, nonzero=True, methods_name=methods_name
)
###################################
#
dx, dy, dz = data.shape

overlap = int(np.round(overlap))
if overlap >= dz - 1:
raise ValueError("Overlap must be less than size of the horizontal detector")
if overlap <= 0:
raise ValueError("Only positive non-zero overlaps are allowed.")
if overlap % 2 != 0:
overlap += 1

if side not in ["left", "right"]:
raise ValueError(
f'The value {side} is invalid, only "left" or "right" strings are accepted'
)

n = dx // 2

out = cp.empty((n, dy, 2 * dz - overlap), dtype=data.dtype)

if side == "left":
Expand Down Expand Up @@ -114,7 +125,12 @@ def sino_360_to_180(


def data_resampler(
data: cp.ndarray, newshape: list, axis: int = 1, interpolation: str = "linear"
data: cp.ndarray,
newshape: list,
axis: int = 1,
interpolation: Literal[
"linear", "nearest", "slinear", "cubic", "quintic", "pchip"
] = "linear",
) -> cp.ndarray:
"""
Down/Up-resampler of the input data implemented through interpn function.
Expand All @@ -131,7 +147,8 @@ def data_resampler(
axis : int, optional
Axis along which the scaling is applied. Defaults to 1.
interpolation : str, optional
Selection of interpolation method. Defaults to 'linear'.
Selection of interpolation method. Defaults to 'linear'. Supported "linear",
"nearest", "slinear", "cubic", "quintic" and "pchip".

Raises
----------
Expand All @@ -141,6 +158,24 @@ def data_resampler(
-------
cp.ndarray: Up/Down-scaled 3D cupy array
"""

### Data and parameters checks ###
methods_name = "data_resampler"
__check_if_data_3D_array(data, methods_name)
__check_if_data_correct_type(
data, accepted_type=["float32", "uint16"], methods_name=methods_name
)
__check_variable_type(newshape, [list], "newshape", [], methods_name)
__check_variable_type(axis, [int], "axis", [0, 1, 2], methods_name)
__check_variable_type(
interpolation,
[str],
"interpolation",
["linear", "nearest", "slinear", "cubic", "quintic", "pchip"],
methods_name,
)
###################################
#
expanded = False
# if 2d data is given it is extended into a 3D array along the vertical dimension
if data.ndim == 2:
Expand All @@ -156,20 +191,18 @@ def data_resampler(
step_x = M / newshape[0]
step_y = Z / newshape[1]
scaled_data = cp.empty((N, newshape[0], newshape[1]), dtype=cp.float32)
elif axis == 1:
if axis == 1:
xaxis = cp.arange(N) - N / 2
yaxis = cp.arange(Z) - Z / 2
step_x = N / newshape[0]
step_y = Z / newshape[1]
scaled_data = cp.empty((newshape[0], M, newshape[1]), dtype=cp.float32)
elif axis == 2:
if axis == 2:
xaxis = cp.arange(N) - N / 2
yaxis = cp.arange(M) - M / 2
step_x = N / newshape[0]
step_y = M / newshape[1]
scaled_data = cp.empty((newshape[0], newshape[1], Z), dtype=cp.float32)
else:
raise ValueError("Only 0,1,2 values for axes are supported")

points = (xaxis, yaxis)

Expand Down
Loading
Loading