from typing import Optional, Union
import numpy as np
from . import _operators
from ._core import Device, get_device
from ._pyclesperanto import _Array as Array
from ._utils import _assert_supported_dtype
def _prepare_array(arr) -> np.ndarray:
"""Converts a given array to a numpy array with C memory layout.
Parameters
----------
arr :
The array to convert.
Returns
-------
np.ndarray
The converted array.
"""
return np.require(arr, None, "C")
def __str__(self) -> str:
"""Returns a string representation of the Array."""
return self.get().__str__()
def __repr__(self) -> str:
"""Returns a string representation of the Array."""
repr_str = self.get().__repr__()
extra_info = f"mtype={self.mtype}"
return repr_str[:-1] + f", {extra_info})"
[docs]
def set(
self,
array: np.ndarray,
origin: Optional[tuple] = None,
region: Optional[tuple] = None,
) -> None:
"""Set the content of the Array to the given numpy array.
Parameters
----------
array : np.ndarray
The array to set from.
origin : tuple, optional
The origin of the region of interest, by default None
region : tuple, optional
The region of interest, by default None
Returns
-------
Array
The array itself.
"""
if not isinstance(array, (np.ndarray, Array)):
array = np.array(array)
if array.dtype != self.dtype:
array = array.astype(self.dtype)
if region and array.size != np.prod(region):
raise IndexError(
f"Value size mismatch the targeted region: {array.size} != {np.prod(region)} ({array.shape} != {tuple(np.squeeze(region))})"
)
elif not region and self.size != array.size:
raise IndexError(
f"Value size mismatch the targeted region: {self.size} != {array.size} ({self.shape} != {array.shape})"
)
self._write(_prepare_array(array), origin, region)
return self
[docs]
def get(
self, origin: Optional[tuple] = None, region: Optional[tuple] = None
) -> np.ndarray:
"""Get the content of the Array into a numpy array.
Parameters
----------
origin : tuple, optional
The origin of the region of interest, by default None
region : tuple, optional
The region of interest, by default None
Returns
-------
np.ndarray
The array itself.
"""
caster = {
"float32": self._read_float32,
"int8": self._read_int8,
"int16": self._read_int16,
"int32": self._read_int32,
# "int64": self._read_int64,
"uint8": self._read_uint8,
"uint16": self._read_uint16,
"uint32": self._read_uint32,
# "uint64": self._read_uint64,
}
return caster[self.dtype.name](origin, region)
def __array__(self, dtype=None) -> np.ndarray:
"""Returns a numpy array representation of the Array."""
if dtype is None:
return self.get()
else:
return self.get().astype(dtype)
[docs]
def to_device(cls, arr, *args, **kwargs):
"""Create an Array object from a numpy array (same shape, dtype, and memory).
Parameters
----------
arr : np.ndarray
The array to convert.
mtype : str, optional
The memory type, by default "buffer"
device : Device, optional
The device, by default None
Returns
-------
Array
The converted array.
"""
if isinstance(arr, Array):
return arr
mtype = kwargs.get("mtype", "buffer")
device = kwargs.get("device", get_device())
return cls.create(arr.shape, arr.dtype, mtype, device).set(arr)
[docs]
def from_array(cls, arr, *args, **kwargs):
"""Create an Array object from a numpy array (same shape, dtype, and memory).
Parameters
----------
arr : np.ndarray
The array to convert.
mtype : str, optional
The memory type, by default "buffer"
device : Device, optional
The device, by default None
Returns
-------
Array
The converted array.
"""
_assert_supported_dtype(arr.dtype)
return cls.to_device(arr, *args, **kwargs)
[docs]
def empty(cls, shape, dtype=float, *args, **kwargs):
"""Create an empty Array object from a shape.
Parameters
----------
shape : tuple, list or np.ndarray
The shape of the array, maximum 3 elements.
dtype : np.dtype, default float
The dtype of the array.
mtype : str, optional
The memory type, by default "buffer"
device : Device, optional
The device, by default None
Returns
-------
Array
A new Array object.
"""
_assert_supported_dtype(dtype)
mtype = kwargs.get("mtype", "buffer")
device = kwargs.get("device", get_device())
return cls.create(shape=shape, dtype=dtype, mtype=mtype, device=device)
[docs]
def empty_like(cls, arr):
"""Create an empty Array object from an other array.
Parameters
----------
arr : np.ndarray or Array or other array-like structure
The array to create like.
Returns
-------
Array
The created array.
"""
_assert_supported_dtype(arr.dtype)
mtype = arr.mtype if isinstance(arr, Array) else "buffer"
device = arr.device if isinstance(arr, Array) else get_device()
return Array.create(arr.shape, arr.dtype, mtype, device)
[docs]
def zeros(cls, shape, dtype=float, *args, **kwargs):
"""Create an Array object full of zeros from a shape.
Parameters
----------
shape : tuple, list or np.ndarray
The shape of the array, maximum 3 elements.
dtype : np.dtype, default float
The dtype of the array.
mtype : str, optional
The memory type, by default "buffer"
device : Device, optional
The device, by default None
Returns
-------
Array
The created array.
"""
_assert_supported_dtype(dtype)
new_array = cls.empty(shape=shape, dtype=dtype, *args, **kwargs)
new_array.fill(0)
return new_array
[docs]
def zeros_like(cls, arr):
"""Create an Array object filled with zeros from an other array.
Parameters
----------
arr : np.ndarray or Array or other array-like structure
The array to create like.
Returns
-------
Array
The created array.
"""
_assert_supported_dtype(arr.dtype)
mtype = arr.mtype if isinstance(arr, Array) else "buffer"
device = arr.device if isinstance(arr, Array) else get_device()
return cls.zeros(shape=arr.shape, dtype=arr.dtype, mtype=mtype, device=device)
[docs]
def T(self):
"""Transpose the Array. Only works for 2D and 3D arrays."""
from ._tier1 import transpose_xy, transpose_xz
if len(self.shape) == 2:
return transpose_xy(self)
elif len(self.shape) == 3:
return transpose_xz(self)
else:
return self
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
if method == "__call__":
func = getattr(Array, f"__{ufunc.__name__}__", None)
if func is not None:
return func(*[Array.to_device(i) for i in inputs], **kwargs)
return NotImplemented
[docs]
def reshape(self, shape):
"""Reshape the Array."""
return self.get().reshape(shape)
# missing operators:
# __array_interface__
# __array_ufunc__
# Add class methods, properties and magic methods
setattr(Array, "T", property(T))
setattr(Array, "set", set)
setattr(Array, "get", get)
setattr(Array, "__array_ufunc__", __array_ufunc__)
setattr(Array, "__str__", __str__)
setattr(Array, "__repr__", __repr__)
setattr(Array, "__array__", __array__)
setattr(Array, "from_array", classmethod(from_array))
setattr(Array, "empty", classmethod(empty))
setattr(Array, "empty_like", classmethod(empty_like))
setattr(Array, "zeros", classmethod(zeros))
setattr(Array, "zeros_like", classmethod(zeros_like))
setattr(Array, "to_device", classmethod(to_device))
setattr(Array, "reshape", reshape)
# Add operations and class methods from _operators module
setattr(Array, "astype", _operators._astype)
setattr(Array, "max", _operators._max)
setattr(Array, "min", _operators._min)
setattr(Array, "sum", _operators._sum)
setattr(Array, "__pos__", _operators.__pos__)
setattr(Array, "__neg__", _operators.__neg__)
setattr(Array, "__add__", _operators.__add__)
setattr(Array, "__iadd__", _operators.__iadd__)
setattr(Array, "__sub__", _operators.__sub__)
setattr(Array, "__div__", _operators.__div__)
setattr(Array, "__truediv__", _operators.__truediv__)
setattr(Array, "__idiv__", _operators.__idiv__)
setattr(Array, "__itruediv__", _operators.__itruediv__)
setattr(Array, "__mul__", _operators.__mul__)
setattr(Array, "__imul__", _operators.__imul__)
setattr(Array, "__gt__", _operators.__gt__)
setattr(Array, "__ge__", _operators.__ge__)
setattr(Array, "__lt__", _operators.__lt__)
setattr(Array, "__le__", _operators.__le__)
setattr(Array, "__eq__", _operators.__eq__)
setattr(Array, "__ne__", _operators.__ne__)
setattr(Array, "__pow__", _operators.__pow__)
setattr(Array, "__ipow__", _operators.__ipow__)
setattr(Array, "_plt_to_png", _operators.__plt_to_png__)
setattr(Array, "_png_to_html", _operators.__png_to_html__)
setattr(Array, "_repr_html_", _operators.__repr_html__)
setattr(Array, "__iter__", _operators.__iter__)
setattr(Array, "__setitem__", _operators.__setitem__)
setattr(Array, "__getitem__", _operators.__getitem__)
# Create Image type
Image = Union[np.ndarray, Array]
[docs]
def is_image(object):
"""Returns True if the given object is an image."""
return (
isinstance(object, np.ndarray)
or isinstance(object, tuple)
or isinstance(object, list)
or isinstance(object, Array)
or str(type(object))
in [
"<class 'cupy._core.core.ndarray'>",
"<class 'dask.array.core.Array'>",
"<class 'xarray.core.dataarray.DataArray'>",
"<class 'resource_backed_dask_array.ResourceBackedDaskArray'>",
"<class 'torch.Tensor'>",
"<class 'pyclesperanto_prototype._tier0._pycl.OCLArray'>",
"<class 'napari.layers._multiscale_data.MultiScaleData'>",
]
)