Performance improvements for makeARGB. Also adding unit tests..

This commit is contained in:
Luke Campagnola 2016-01-10 23:08:19 -08:00
parent 9e8c2082ed
commit 99aa4cfdd3
2 changed files with 95 additions and 9 deletions

View File

@ -824,9 +824,14 @@ def rescaleData(data, scale, offset, dtype=None):
setConfigOptions(useWeave=False)
#p = np.poly1d([scale, -offset*scale])
#data = p(data).astype(dtype)
d2 = data-offset
np.multiply(d2, scale, out=d2, casting="unsafe")
#d2 = p(data)
d2 = data - float(offset)
d2 *= scale
# Clip before converting dtype to avoid overflow
if dtype.kind in 'ui':
lim = np.iinfo(dtype)
d2 = np.clip(d2, lim.min, lim.max)
data = d2.astype(dtype)
return data
@ -875,8 +880,8 @@ def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False):
channel). The use of this feature requires that levels.shape[0] == data.shape[-1].
scale The maximum value to which data will be rescaled before being passed through the
lookup table (or returned if there is no lookup table). By default this will
be set to the length of the lookup table, or 256 is no lookup table is provided.
For OpenGL color specifications (as in GLColor4f) use scale=1.0
be set to the length of the lookup table, or 255 if no lookup table is provided.
For OpenGL color specifications (as in GLColor4f) use scale=1.0.
lut Optional lookup table (array with dtype=ubyte).
Values in data will be converted to color by indexing directly from lut.
The output data shape will be input.shape + lut.shape[1:].
@ -884,7 +889,7 @@ def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False):
Note: the output of makeARGB will have the same dtype as the lookup table, so
for conversion to QImage, the dtype must be ubyte.
Lookup tables can be built using GradientWidget.
Lookup tables can be built using ColorMap or GradientWidget.
useRGBA If True, the data is returned in RGBA order (useful for building OpenGL textures).
The default is False, which returns in ARGB order for use with QImage
(Note that 'ARGB' is a term used by the Qt documentation; the _actual_ order
@ -918,6 +923,13 @@ def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False):
scale = lut.shape[0]
else:
scale = 255.
if lut is not None:
dtype = lut.dtype
elif scale == 255:
dtype = np.ubyte
else:
dtype = np.float
## Apply levels if given
if levels is not None:
@ -931,16 +943,26 @@ def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False):
minVal, maxVal = levels[i]
if minVal == maxVal:
maxVal += 1e-16
newData[...,i] = rescaleData(data[...,i], scale/(maxVal-minVal), minVal, dtype=int)
newData[...,i] = rescaleData(data[...,i], scale/(maxVal-minVal), minVal, dtype=dtype)
data = newData
else:
minVal, maxVal = levels
if minVal == maxVal:
maxVal += 1e-16
if maxVal == minVal:
data = rescaleData(data, 1, minVal, dtype=int)
data = rescaleData(data, 1, minVal, dtype=dtype)
else:
data = rescaleData(data, scale/(maxVal-minVal), minVal, dtype=int)
lutSize = 2**(data.itemsize*8)
if data.dtype in (np.ubyte, np.uint16) and data.size > lutSize:
# Rather than apply scaling to image, scale the LUT for better performance.
ind = np.arange(lutSize)
indr = rescaleData(ind, scale/(maxVal-minVal), minVal, dtype=dtype)
if lut is None:
lut = indr
else:
lut = lut[indr]
else:
data = rescaleData(data, scale/(maxVal-minVal), minVal, dtype=dtype)
profile()

View File

@ -111,6 +111,70 @@ def test_subArray():
assert np.all(bb == cc)
def test_rescaleData():
dtypes = map(np.dtype, ('ubyte', 'uint16', 'byte', 'int16', 'int', 'float'))
for dtype1 in dtypes:
for dtype2 in dtypes:
data = (np.random.random(size=10) * 2**32 - 2**31).astype(dtype1)
for scale, offset in [(10, 0), (10., 0.), (1, -50), (0.2, 0.5), (0.001, 0)]:
if dtype2.kind in 'iu':
lim = np.iinfo(dtype2)
lim = lim.min, lim.max
else:
lim = (-np.inf, np.inf)
s1 = np.clip(float(scale) * (data-float(offset)), *lim).astype(dtype2)
s2 = pg.rescaleData(data, scale, offset, dtype2)
assert s1.dtype == s2.dtype
if dtype2.kind in 'iu':
assert np.all(s1 == s2)
else:
assert np.allclose(s1, s2)
def test_makeARGB():
# uint8 data tests
im1 = np.array([[1,2,3], [4,5,8]], dtype='ubyte')
im2, alpha = pg.makeARGB(im1, levels=(0, 6))
assert im2.dtype == np.ubyte
assert alpha == False
assert np.all(im2[...,3] == 255)
assert np.all(im2[...,:3] == np.array([[42, 85, 127], [170, 212, 255]], dtype=np.ubyte)[...,np.newaxis])
im3, alpha = pg.makeARGB(im1, levels=(0.0, 6.0))
assert im3.dtype == np.ubyte
assert alpha == False
assert np.all(im3 == im2)
im2, alpha = pg.makeARGB(im1, levels=(2, 10))
assert im2.dtype == np.ubyte
assert alpha == False
assert np.all(im2[...,3] == 255)
assert np.all(im2[...,:3] == np.array([[0, 0, 31], [63, 95, 191]], dtype=np.ubyte)[...,np.newaxis])
im2, alpha = pg.makeARGB(im1, levels=(2, 10), scale=1.0)
assert im2.dtype == np.float
assert alpha == False
assert np.all(im2[...,3] == 1.0)
assert np.all(im2[...,:3] == np.array([[0, 0, 31], [63, 95, 191]], dtype=np.ubyte)[...,np.newaxis])
# uint8 input + uint8 LUT
lut = np.arange(512).astype(np.ubyte)[::2][::-1]
im2, alpha = pg.makeARGB(im1, lut=lut, levels=(2, 10))
assert im2.dtype == np.ubyte
assert alpha == False
assert np.all(im2[...,3] == 255)
assert np.all(im2[...,:3] == np.array([[0, 0, 31], [63, 95, 191]], dtype=np.ubyte)[...,np.newaxis])
# uint8 data + uint16 LUT
# uint8 data + float LUT
# uint16 data tests
im1 = np.array([[1,2,3], [4,5,8]], dtype='ubyte')
if __name__ == '__main__':
test_interpolateArray()