Permalink
Browse files

BUG: fix pilutil.bytescale scaling behavior. Closes #1748.

Thanks to kingson for the patch.
  • Loading branch information...
1 parent 43c7982 commit 1aee114cd0628f77b512e44b8dffc8ee6634291e @rgommers rgommers committed Oct 30, 2012
Showing with 46 additions and 19 deletions.
  1. +34 −16 scipy/misc/pilutil.py
  2. +12 −3 scipy/misc/tests/test_pilutil.py
View
@@ -30,23 +30,27 @@ def bytescale(data, cmin=None, cmax=None, high=255, low=0):
"""
Byte scales an array (image).
+ Byte scaling means converting the input image to uint8 dtype and scaling
+ the range to ``(low, high)`` (default 0-255).
+ If the input image already has dtype uint8, no scaling is done.
+
Parameters
----------
data : ndarray
PIL image data array.
- cmin : Scalar
- Bias scaling of small values, Default is data.min().
- cmax : scalar
- Bias scaling of large values, Default is data.max().
- high : scalar
- Scale max value to `high`.
- low : scalar
- Scale min value to `low`.
+ cmin : scalar, optional
+ Bias scaling of small values. Default is ``data.min()``.
+ cmax : scalar, optional
+ Bias scaling of large values. Default is ``data.max()``.
+ high : scalar, optional
+ Scale max value to `high`. Default is 255.
+ low : scalar, optional
+ Scale min value to `low`. Default is 0.
Returns
-------
- img_array : ndarray
- Bytescaled array.
+ img_array : uint8 ndarray
+ The byte-scaled array.
Examples
--------
@@ -69,12 +73,26 @@ def bytescale(data, cmin=None, cmax=None, high=255, low=0):
"""
if data.dtype == uint8:
return data
- high = high - low
- if cmin is None: cmin = data.min()
- if cmax is None: cmax = data.max()
- scale = high *1.0 / (cmax-cmin or 1)
- bytedata = ((data*1.0-cmin)*scale + 0.4999).astype(uint8)
- return bytedata + cast[uint8](low)
+
+ if high < low:
+ raise ValueError("`high` should be larger than `low`.")
+
+ if cmin is None:
+ cmin = data.min()
+ if cmax is None:
+ cmax = data.max()
+
+ cscale = cmax - cmin
+ if cscale < 0:
+ raise ValueError("`cmax` should be larger than `cmin`.")
+ elif cscale == 0:
+ cscale = 1
+
+ scale = float(high - low) / cscale
+ bytedata = (data * 1.0 - cmin) * scale + 0.4999
+ bytedata[bytedata > high] = high
+ bytedata[bytedata < 0] = 0
+ return cast[uint8](bytedata) + cast[uint8](low)
def imread(name,flatten=0):
"""
@@ -39,10 +39,19 @@ def test_imresize3(self):
assert_equal(im2.shape, (30,60))
def test_bytescale(self):
- x = np.array([0,1,2],np.uint8)
+ x = np.array([0,1,2], np.uint8)
y = np.array([0,1,2])
- assert_equal(misc.bytescale(x),x)
- assert_equal(misc.bytescale(y),[0,127,255])
+ assert_equal(misc.bytescale(x), x)
+ assert_equal(misc.bytescale(y), [0,127,255])
+
+ def test_bytescale_keywords(self):
+ x = np.array([40, 60, 120, 200, 300, 500])
+ res_lowhigh = misc.bytescale(x, low=10, high=143)
+ assert_equal(res_lowhigh, [10, 16, 33, 56, 85, 143])
+ res_cmincmax = misc.bytescale(x, cmin=60, cmax=300)
+ assert_equal(res_cmincmax, [0, 0, 64, 149, 255, 255])
+
+ assert_equal(misc.bytescale(np.array([3, 3, 3]), low=4), [4, 4, 4])
def tst_fromimage(filename, irange):
fp = open(filename, "rb")

0 comments on commit 1aee114

Please sign in to comment.