mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-01 06:34:23 +08:00
Rewrite convert function
This commit is contained in:
+114
-70
@@ -10,8 +10,8 @@ dtype_range = {np.uint8: (0, 255),
|
||||
np.uint16: (0, 65535),
|
||||
np.int8: (-128, 127),
|
||||
np.int16: (-32768, 32767),
|
||||
np.float32: (0, 1),
|
||||
np.float64: (0, 1)}
|
||||
np.float32: (-1, 1),
|
||||
np.float64: (-1, 1)}
|
||||
|
||||
integer_types = (np.uint8, np.uint16, np.int8, np.int16)
|
||||
|
||||
@@ -20,19 +20,24 @@ _supported_types = (np.uint8, np.uint16, np.uint32,
|
||||
np.float32, np.float64)
|
||||
|
||||
if np.__version__ >= "1.6.0":
|
||||
dtype_range[np.float16] = (0, 1)
|
||||
dtype_range[np.float16] = (-1, 1)
|
||||
_supported_types += (np.float16, )
|
||||
|
||||
|
||||
def convert(image, dtype, force_copy=False):
|
||||
def convert(image, dtype, force_copy=False, uniform=False):
|
||||
"""
|
||||
Convert an image to the requested data-type.
|
||||
|
||||
Warnings are issued in case of precision loss, or when
|
||||
negative values have to be scaled into the positive domain.
|
||||
Floating point values must be in the range [0.0, 1.0].
|
||||
|
||||
Floating point values are expected to be normalized. They will be
|
||||
clipped to the range [0.0, 1.0] or [-1.0, 1.0] when converting to
|
||||
unsigned respectively signed integers.
|
||||
|
||||
Numbers are not shifted to the negative side when converting from
|
||||
floating point or unsigned integer types to signed integer types.
|
||||
unsigned to signed integer types. Negative values will be clipped from
|
||||
signed integers when converting to unsigned integers.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -42,6 +47,9 @@ def convert(image, dtype, force_copy=False):
|
||||
Target data-type.
|
||||
force_copy : bool
|
||||
Force a copy of the data, irrespective of its current dtype.
|
||||
uniform : bool
|
||||
Quantize the floating point range to integer range uniformly instead
|
||||
of scaling and rounding floating point values to the nearest integers.
|
||||
|
||||
"""
|
||||
image = np.asarray(image)
|
||||
@@ -74,93 +82,129 @@ def convert(image, dtype, force_copy=False):
|
||||
# Return first of `dtypes` with itemsize greater than `itemsize`
|
||||
return next(dt for dt in dtypes if itemsize < np.dtype(dt).itemsize)
|
||||
|
||||
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, copy=True):
|
||||
# Scale unsigned 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:
|
||||
return a.copy() if copy else a
|
||||
elif n > m:
|
||||
# downscale with precision loss
|
||||
prec_loss()
|
||||
if copy:
|
||||
b = np.empty(a.shape, _dtype2(kind, m))
|
||||
np.divide(a, 2**(n - m), out=b)
|
||||
return b
|
||||
else:
|
||||
a //= 2**(n - m)
|
||||
return a
|
||||
elif 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)
|
||||
return b
|
||||
else:
|
||||
a = np.array(a, _dtype2(kind, m, a.dtype.itemsize), copy=False)
|
||||
a *= (2**m - 1) / (2**n - 1)
|
||||
return a
|
||||
else:
|
||||
# upscale to a multiple of n bits,
|
||||
# then downscale with precision loss
|
||||
prec_loss()
|
||||
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)
|
||||
b //= 2**(o - m)
|
||||
return b
|
||||
else:
|
||||
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
|
||||
|
||||
if kind_in == 'f':
|
||||
if np.min(image) < 0 or np.max(image) > 1:
|
||||
raise ValueError("Images of type float must be between 0 and 1")
|
||||
if kind == 'f':
|
||||
# floating point -> floating point
|
||||
if itemsize_in > itemsize:
|
||||
prec_loss()
|
||||
return dtype(image)
|
||||
|
||||
# floating point -> integer
|
||||
prec_loss()
|
||||
# use float type that can represent output integer type
|
||||
image = np.array(image, _dtype(itemsize, dtype_in,
|
||||
np.float32, np.float64))
|
||||
image *= np.iinfo(dtype).max + 1
|
||||
np.clip(image, 0, np.iinfo(dtype).max, out=image)
|
||||
if not uniform:
|
||||
if kind == 'u':
|
||||
image *= np.iinfo(dtype).max
|
||||
else:
|
||||
image *= np.iinfo(dtype).max - np.iinfo(dtype).min
|
||||
image -= 1.0
|
||||
image /= 2.0
|
||||
np.rint(image, out=image)
|
||||
np.clip(image, np.iinfo(dtype).min, np.iinfo(dtype).max, out=image)
|
||||
elif kind == 'u':
|
||||
image *= np.iinfo(dtype).max + 1
|
||||
np.clip(image, 0, np.iinfo(dtype).max, out=image)
|
||||
else:
|
||||
image += 1.0
|
||||
image *= (np.iinfo(dtype).max - np.iinfo(dtype).min + 1.0) / 2.0
|
||||
np.clip(image, np.iinfo(dtype).min, np.iinfo(dtype).max, out=image)
|
||||
image -= np.iinfo(dtype).min
|
||||
return dtype(image)
|
||||
|
||||
if kind == 'f':
|
||||
# integer -> floating point
|
||||
if itemsize_in >= itemsize:
|
||||
prec_loss()
|
||||
# use float type that can represent input integers
|
||||
# use float type that can exactly represent input integers
|
||||
image = np.array(image, _dtype(itemsize_in, dtype,
|
||||
np.float32, np.float64))
|
||||
if np.iinfo(dtype_in).min:
|
||||
sign_loss()
|
||||
image -= np.iinfo(dtype_in).min
|
||||
image /= np.iinfo(dtype_in).max - np.iinfo(dtype_in).min
|
||||
return dtype(image)
|
||||
if kind_in == 'u':
|
||||
# unsigned integer -> integer
|
||||
shift = 1 if kind == 'i' else 0
|
||||
if itemsize_in > itemsize:
|
||||
prec_loss()
|
||||
image = image >> 8 * (itemsize_in - itemsize) + shift
|
||||
return dtype(image)
|
||||
result = dtype(image)
|
||||
result <<= 8 * (itemsize - itemsize_in) - shift
|
||||
if itemsize - itemsize_in == 3:
|
||||
# uint8 -> (u)int32
|
||||
# hint: 4294967295 == (255 << 24) + (255 << 16) + (255 << 8) + 255
|
||||
image = dtype(image)
|
||||
image *= 2**16 + 2**8 + 1
|
||||
if shift:
|
||||
result += image >> shift
|
||||
if kind_in == 'u':
|
||||
image /= np.iinfo(dtype_in).max
|
||||
# DirectX uses this conversion also for signed ints
|
||||
#if np.iinfo(dtype_in).min:
|
||||
# np.maximum(image, -1.0, out=image)
|
||||
else:
|
||||
result += image
|
||||
return dtype(result)
|
||||
image *= 2.0
|
||||
image += 1.0
|
||||
image /= np.iinfo(dtype_in).max - np.iinfo(dtype_in).min
|
||||
return dtype(image)
|
||||
|
||||
if kind_in == 'u':
|
||||
if kind == 'i':
|
||||
# unsigned integer -> signed integer
|
||||
image = _scale(image, 8*itemsize_in, 8*itemsize-1)
|
||||
return image.view(dtype)
|
||||
else:
|
||||
# unsigned integer -> unsigned integer
|
||||
return _scale(image, 8*itemsize_in, 8*itemsize)
|
||||
|
||||
if kind == 'u':
|
||||
# signed integer -> unsigned integer
|
||||
sign_loss()
|
||||
# use next higher precision signed integer type
|
||||
image = np.array(image, _dtype(itemsize_in,
|
||||
np.int16, np.int32, np.int64))
|
||||
image -= np.iinfo(dtype_in).min
|
||||
if itemsize_in == itemsize:
|
||||
return dtype(image)
|
||||
if itemsize_in > itemsize:
|
||||
prec_loss()
|
||||
image >>= 8 * (itemsize_in - itemsize)
|
||||
return dtype(image)
|
||||
result = dtype(image)
|
||||
result <<= 8 * (itemsize - itemsize_in)
|
||||
if itemsize - itemsize_in == 3:
|
||||
# int8 -> uint32
|
||||
image = dtype(image)
|
||||
image *= 2**16 + 2**8 + 1
|
||||
result += dtype(image)
|
||||
image = _scale(image, 8*itemsize_in-1, 8*itemsize)
|
||||
result = np.empty(image.shape, dtype)
|
||||
np.maximum(image, 0, out=result)
|
||||
return result
|
||||
if kind == 'i':
|
||||
# signed integer -> signed integer
|
||||
if itemsize_in > itemsize:
|
||||
prec_loss()
|
||||
return dtype(image // 2**(8 * (itemsize_in - itemsize)))
|
||||
# use next higher precision signed integer type
|
||||
image = np.array(image, _dtype(itemsize_in,
|
||||
np.int16, np.int32, np.int64))
|
||||
image -= np.iinfo(dtype_in).min
|
||||
# use next higher precision signed integer type
|
||||
result = np.array(image, _dtype(image.itemsize, np.int32, np.int64))
|
||||
result *= 2**(8 * (itemsize - itemsize_in))
|
||||
if itemsize - itemsize_in == 3:
|
||||
# int8 -> int32
|
||||
image = dtype(image)
|
||||
image *= 2**16 + 2**8 + 1
|
||||
result += image
|
||||
result += np.iinfo(dtype).min
|
||||
return dtype(result)
|
||||
|
||||
# signed integer -> signed integer
|
||||
if itemsize_in > itemsize:
|
||||
return _scale(image, 8*itemsize_in-1, 8*itemsize-1)
|
||||
image = image.astype(_dtype2('i', itemsize*8))
|
||||
image -= np.iinfo(dtype_in).min
|
||||
image = _scale(image, 8*itemsize_in, 8*itemsize, copy=False)
|
||||
image += np.iinfo(dtype).min
|
||||
return image
|
||||
|
||||
|
||||
def img_as_float(image, force_copy=False):
|
||||
|
||||
Reference in New Issue
Block a user