ENH: Add dtype converters.

This commit is contained in:
Stefan van der Walt
2011-09-26 16:34:00 -07:00
parent b0798eb55f
commit dff2ee74b0
3 changed files with 187 additions and 0 deletions
+2
View File
@@ -45,3 +45,5 @@ def get_log(name):
import logging, sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
return logging.getLogger(name)
from util.dtype import *
+148
View File
@@ -0,0 +1,148 @@
from __future__ import division
import numpy as np
__all__ = ['img_as_float', 'img_as_int', 'img_as_uint']
from .. import get_log
log = get_log('dtype_converter')
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)}
integer_types = (np.uint8, np.int16, np.int8, np.int16)
def _convert(image, dtype, prec_loss):
"""
Convert an image to the requested data-type.
Warnings are issues in case of precision loss, or when
negative values have to be scaled into the positive domain.
Parameters
----------
image : ndarray
Input image.
dtype : dtype
Target data-type.
prec_loss : tuple
List of input data-types that, when converted to `dtype`,
would lose precision.
"""
image = np.asarray(image)
dtype_in = image.dtype.type
if dtype_in == dtype:
return image
if dtype_in in prec_loss:
log.warn('Possible precision loss, converting from '
'%s to %s' % (np.dtype(dtype_in), np.dtype(dtype)))
try:
imin, imax = dtype_range[dtype_in]
omin, omax = dtype_range[dtype]
except KeyError:
raise ValueError("Unsure how to convert %s to %s." % \
(np.dtype(dtype_in), np.dtype(dtype)))
sign_loss = (np.sign(imin) == -1) and (np.sign(omin) != -1)
if sign_loss:
log.warn('Possible sign loss when converting '
'negative image of type %s to positive '
'image of type %s.' % (np.dtype(dtype_in), np.dtype(dtype)))
# If input type is non-negative, or if
# converting to a positive-only type, then we
# there's no need to shift numbers to the negative side
if sign_loss or np.sign(imin) != -1:
shift = 0
omin = 0
else:
shift = omin
scale = (omax - omin) / (imax - imin)
if dtype in integer_types:
round_fn = np.round
else:
round_fn = lambda x: x
# Do scaling/shifting calculations in floating point
image = image.astype(np.float64)
out = np.empty_like(image, dtype=dtype)
out[:] = round_fn((image - imin) * scale + shift)
return out
def img_as_float(image):
"""Convert an image to double-precision floating point format.
Parameters
----------
image : ndarray
Input image.
Returns
-------
out : ndarray of float64
Output image.
Notes
-----
The range of a floating point image is [0, 1].
Negative input values will be shifted to the positive domain.
"""
prec_loss = ()
return _convert(image, np.float64, prec_loss)
def img_as_uint(image):
"""Convert an image to 64-bit unsigned integer format.
Parameters
----------
image : ndarray
Input image.
Returns
-------
out : ndarray of uint64
Output image.
Notes
-----
Negative input values will be shifted to the positive domain.
"""
prec_loss = (np.float32, np.float64)
return _convert(image, np.uint16, prec_loss)
def img_as_int(image):
"""Convert an image to 64-bit signed integer format.
Parameters
----------
image : ndarray
Input image.
Returns
-------
out : ndarray of uint64
Output image.
Notes
-----
If the input data-type is positive-only (e.g., uint8), then
the output image will still only have positive values.
"""
prec_loss = (np.float32, np.float64, np.uint16)
return _convert(image, np.int16, prec_loss)
+37
View File
@@ -0,0 +1,37 @@
import numpy as np
from numpy.testing import assert_equal
from scikits.image import img_as_int, img_as_float, img_as_uint
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)}
def _verify_range(msg, x, vmin, vmax):
assert_equal(x[0], vmin)
assert_equal(x[-1], vmax)
def test_range():
for dtype in dtype_range:
imin, imax = dtype_range[dtype]
x = np.linspace(imin, imax, 10).astype(dtype)
for (f, dt) in [(img_as_int, np.int16),
(img_as_float, np.float64),
(img_as_uint, np.uint16)]:
y = f(x)
omin, omax = dtype_range[dt]
if imin == 0:
omin = 0
yield _verify_range, \
"From %s to %s" % (np.dtype(dtype), np.dtype(dt)), \
y, omin, omax
if __name__ == '__main__':
np.testing.run_module_suite()