mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-30 20:54:26 +08:00
ENH: Add dtype converters.
This commit is contained in:
@@ -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 *
|
||||
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user