Source code for Stoner.Image.util

# -*- coding: utf-8 -*-
"""Code adapted from skimage module."""

from __future__ import division

__all__ = ["sign_loss", "prec_loss", "dtype_range", "_dtype", "_dtype2"]
from warnings import warn

import numpy as np

from ..tools.classes import Options

dtype_range = {
    np.bool_: (False, True),
    np.uint8: (0, 255),
    np.uint16: (0, 65535),
    np.int8: (-128, 127),
    np.int16: (-32768, 32767),
    np.int64: (-(2**63), 2**63 - 1),
    np.uint64: (0, 2**64 - 1),
    np.int32: (-(2**31), 2**31 - 1),
    np.uint32: (0, 2**32 - 1),
    np.float32: (-1.0, 1.0),
    np.float64: (-1.0, 1.0),
}

if float(np.version.version.split(".")[1]) < 1.24:
    dtype_range[np.bool8] = (False, True)

integer_types = (np.uint8, np.uint16, np.int8, np.int16)

_supported_types = (
    np.bool_,
    np.uint8,
    np.uint16,
    np.uint32,
    np.uint64,
    np.int8,
    np.int16,
    np.int32,
    np.int64,
    np.float32,
    np.float64,
)

dtype_range[np.float16] = (-1, 1)
_supported_types += (np.float16,)


[docs]def sign_loss(dtypeobj_in, dtypeobj): """Warn over loss of sign information when converting image.""" if Options().warnings: warn( "Possible sign loss when converting negative image of type " f"{dtypeobj_in} to positive image of type {dtypeobj}." )
[docs]def prec_loss(dtypeobj_in, dtypeobj): """Warn over precision loss when converting image.""" if Options().warnings: warn(f"Possible precision loss when converting from {dtypeobj_in} to {dtypeobj}")
[docs]def _dtype(itemsize, *dtypes): """Return first of `dtypes` with itemsize greater than `itemsize.""" try: ret = next(dt for dt in dtypes if itemsize < np.dtype(dt).itemsize) except StopIteration: ret = dtypes[0] return ret
[docs]def _dtype2(kind, bits, itemsize=1): """Return dtype of `kind` that can store a `bits` wide unsigned int.""" c = lambda x, y: x <= y if kind == "u" else x < y s = next(i for i in (itemsize,) + (2, 4, 8) if c(bits, i * 8)) return np.dtype(kind + str(s))
def _scale(a, n, m, dtypeobj_in, dtypeobj, copy=True): """Scaleunsigned/positive integers from n to m bits. Numbers can be represented exactly only if m is a multiple of n Output array is of same kind as input. """ kind = a.dtype.kind if n > m and a.max() <= 2**m: mnew = int(np.ceil(m / 2) * 2) if mnew > m: dtype = f"int{mnew}" else: dtype = f"uint{mnew}" n = int(np.ceil(n / 2) * 2) msg = f"Downcasting {a.dtype} to {dtype} without scaling because max value {a.max()} fits in{dtype}" if Options().warnings: warn(msg) return a.astype(_dtype2(kind, m)) if n == m: return a.copy() if copy else a if n > m: # downscale with precision loss prec_loss(dtypeobj_in, dtypeobj) if copy: b = np.empty(a.shape, _dtype2(kind, m)) np.floor_divide(a, 2 ** (n - m), out=b, dtype=a.dtype, casting="unsafe") return b a //= 2 ** (n - m) return a if m % n == 0: # exact upscale to a multiple of n bits if copy: b = np.empty(a.shape, _dtype2(kind, m)) np.multiply(a, (2**m - 1) // (2**n - 1), out=b, dtype=b.dtype) return b a = np.array(a, _dtype2(kind, m, a.dtype.itemsize), copy=False) a *= (2**m - 1) // (2**n - 1) return a # upscale to a multiple of n bits, # then downscale with precision loss prec_loss(dtypeobj_in, dtypeobj) o = (m // n + 1) * n if copy: b = np.empty(a.shape, _dtype2(kind, o)) np.multiply(a, (2**o - 1) // (2**n - 1), out=b, dtype=b.dtype) b //= 2 ** (o - m) return b a = np.array(a, _dtype2(kind, o, a.dtype.itemsize), copy=False) a *= (2**o - 1) // (2**n - 1) a //= 2 ** (o - m) return a