"""
NumPy array conversion utilities for integer and floating-point image data.
"""
from __future__ import annotations
import numpy as np
from machinevisiontoolbox.mvtb_types import Dtype
[docs]
def int_image(
image: np.ndarray, intclass: Dtype = "uint8", maxintval: int | None = None
) -> np.ndarray:
"""
Convert image to integer type
:param image: input image
:type image: ndarray(H,W), ndarray(H,W,P)
:param intclass: integer class to convert to, the name of any integer
class supported by NumPy, defaults to ``'uint8'``
:type intclass: str or NumPy dtype
:param maxintval: maximum value of integer, defaults to maximum positive
value of ``image`` datatype
:type maxintval: int
:return: image with integer pixel types
:rtype: ndarray(H,W), ndarray(H,W,P)
Return a copy of the image as a NumPy array with pixel values scaled and
converted to the integer class ``intclass``. If the input image is:
* a floating point class, the pixel values are scaled from an input range of
[0.0, 1.0] to a range spanning zero to the maximum positive value of
``intclass``.
* an integer class, the pixels are scaled and cast to ``intclass``. The
scale factor is the ratio of ``maxintval`` to the maximum positive value
of ``inclass``.
* the boolean class, False is mapped to zero and True is mapped to the
maximum positive value of ``inclass``.
Example:
.. runblock:: pycon
>>> from machinevisiontoolbox import int_image
>>> import numpy as np
>>> im = np.array([[1,2],[3,4]], 'uint8')
>>> int_image(im, 'int16')
>>> im = np.array([[False, True],[True, False]])
>>> int_image(im)
.. note:: Works for greyscale or color (arbitrary number of planes) image
:references:
- |RVC3|, Section 10.1.
:seealso: :func:`float_image`
"""
intclass = np.dtype(intclass)
if np.issubdtype(image.dtype, bool):
return image.astype(intclass) * np.iinfo(intclass).max
elif np.issubdtype(image.dtype, np.floating):
# rescale to integer
scaled = image * np.float64(np.iinfo(intclass).max)
return np.rint(scaled).astype(intclass)
elif np.issubdtype(image.dtype, np.integer):
# scale and cast to different integer type
if maxintval is None:
maxintval = np.iinfo(image.dtype).max
image = image * (np.iinfo(intclass).max / maxintval)
return image.astype(intclass)
else:
raise ValueError("unsupported image data type")
[docs]
def float_image(
image: np.ndarray, floatclass: Dtype = "float32", maxintval: int | None = None
) -> np.ndarray:
"""
Convert image to float type
:param image: input image
:type image: ndarray(H,W), ndarray(H,W,P)
:param floatclass: 'single', 'double', 'float32' [default], 'float64'
:type floatclass: str or NumPy dtype
:param maxintval: maximum value of integer, defaults to maximum positive
value of ``image`` datatype
:type maxintval: int
:return: image with floating point pixel types
:rtype: ndarray(H,W), ndarray(H,W,P)
Return a copy of the image as a NumPy array with pixels scaled and converted
to the float class ``floatclass`` with pixel values spanning the range 0.0 to
1.0. If the input image is:
* an integer class, the pixel values are scaled from an input range spanning
zero to ``maxintval`` to [0.0, 1.0]
* a floating point class, the pixels are cast to change type but not their
value.
* the boolean class, False is mapped to 0.0 and True is mapped to 1.0.
Example:
.. runblock:: pycon
>>> from machinevisiontoolbox import float_image
>>> import numpy as np
>>> im = np.array([[1,2],[3,4]], 'uint8')
>>> float_image(im)
>>> im = np.array([[False, True],[True, False]])
>>> float_image(im)
.. note:: Works for greyscale or color (arbitrary number of planes) image
:references:
- |RVC3|, Section 10.1.
:seealso: :func:`int_image`
"""
floatclass = np.dtype(floatclass)
if not np.issubdtype(floatclass, np.floating):
raise ValueError("bad float type")
if np.issubdtype(image.dtype, np.integer):
# rescale the pixel values
if maxintval is None:
maxintval = np.iinfo(image.dtype).max
return image.astype(floatclass) / maxintval
elif np.issubdtype(image.dtype, np.floating):
# cast to different float type
return image.astype(floatclass)
elif np.issubdtype(image.dtype, bool):
return image.astype(floatclass)
else:
raise ValueError("unsupported image data type")
def image_to_dtype(image: np.ndarray, dtype: str | np.dtype) -> np.ndarray:
dtype = np.dtype(dtype) # convert to dtype if it's a string
if np.issubdtype(dtype, np.integer):
return int_image(dtype)
elif np.issubdtype(dtype, np.floating):
return float_image(dtype)